check and install needed packages. Load the libraries and functions.

packages.used=c("rvest", "tibble", "qdap", 
                "sentimentr", "gplots", "dplyr",
                "tm", "syuzhet", "factoextra", 
                "beeswarm", "scales", "RColorBrewer",
                "RANN", "tm", "topicmodels")
# check packages that need to be installed.
packages.needed=setdiff(packages.used, 
                        intersect(installed.packages()[,1], 
                                  packages.used))
# install additional packages
if(length(packages.needed)>0){
  install.packages(packages.needed, dependencies = TRUE)
}
# load packages
library("rvest")
library("tibble")
library("qdap")
library("sentimentr")
library("gplots")
library("dplyr")
library("tm")
library("syuzhet")
library("factoextra")
library("beeswarm")
library("scales")
library("RColorBrewer")
library("RANN")
library("tm")
library("topicmodels")
source("../lib/plotstacked.R")
source("../lib/speechFuncs.R")

Scrap speech URLs from http://www.presidency.ucsb.edu/.

part1:data preprocessing

What we do the first part is to grab data and then combine the data into one data.frame for future use.

Following the example of Jerid Francom, we used Selectorgadget to choose the links we would like to scrap. For this project, we selected all inaugural addresses of past presidents, nomination speeches of major party candidates and farewell addresses. We also included several public speeches from Donald Trump for our textual analysis of presidential speeches.

### Inauguaral speeches
main.page <- read_html(x = "http://www.presidency.ucsb.edu/inaugurals.php")
# Get link URLs
# f.speechlinks is a function for extracting links from the list of speeches. 
inaug=f.speechlinks(main.page)
#head(inaug)
as.Date(inaug[,1], format="%B %e, %Y")
 [1] "1789-04-30" "1793-03-04" "1797-03-04" "1801-03-04" "1805-03-04" "1809-03-04" "1813-03-04"
 [8] "1817-03-04" "1821-03-04" "1825-03-04" "1829-03-04" "1833-03-04" "1837-03-04" "1841-03-04"
[15] "1845-03-04" "1849-03-05" "1853-03-04" "1857-03-04" "1861-03-04" "1865-03-04" "1869-03-04"
[22] "1873-03-04" "1877-03-05" "1881-03-04" "1885-03-04" "1889-03-04" "1893-03-04" "1897-03-04"
[29] "1901-03-04" "1905-03-04" "1909-03-04" "1913-03-04" "1917-03-04" "1921-03-04" "1925-03-04"
[36] "1929-03-04" "1933-03-04" "1937-01-20" "1941-01-20" "1945-01-20" "1949-01-20" "1953-01-20"
[43] "1957-01-21" "1961-01-20" "1965-01-20" "1969-01-20" "1973-01-20" "1977-01-20" "1981-01-20"
[50] "1985-01-21" "1989-01-20" "1993-01-20" "1997-01-20" "2001-01-20" "2005-01-20" "2009-01-20"
[57] "2013-01-21" "2017-01-20" NA          
inaug=inaug[-nrow(inaug),] # remove the last line, irrelevant due to error.

Read data and then we assemble all scrapped speeches into one list.

inaug.list=read.csv("../data/inauglist.csv", stringsAsFactors = FALSE)

We assemble all scrapped speeches into one list. Note here that we don’t have the full text yet, only the links to full text transcripts.

speech.list=inaug.list
speech.list$type=rep("inaug", nrow(inaug.list))
speech.url=inaug
speech.list=cbind(speech.list, speech.url)

Based on the list of speeches, we scrap the main text part of the transcript’s html page. For simple html pages of this kind, Selectorgadget is very convenient for identifying the html node that rvest can use to scrap its content. For reproducibility, we also save our scrapped speeches into our local folder as individual speech files.

# Loop over each row in speech.list
speech.list$fulltext=NA
for(i in seq(nrow(speech.list))) {
  text <- read_html(speech.list$urls[i]) %>% # load the page
    html_nodes(".displaytext") %>% # isloate the text
    html_text() # get the text
  speech.list$fulltext[i]=text
  # Create the file name
  filename <- paste0("../data/fulltext/", 
                     speech.list$type[i],
                     speech.list$File[i], "-", 
                     speech.list$Term[i], ".txt")
  sink(file = filename) %>% # open file to write 
  cat(text)  # write the file
  sink() # close the file
}

Trump, as president-elect that has not been a politician, do not have a lot of formal speeches yet. For our textual analysis, we manually add several public transcripts from Trump: + [Transcript: Donald Trump’s full immigration speech, annotated. LA Times, 08/31/2016] (http://www.latimes.com/politics/la-na-pol-donald-trump-immigration-speech-transcript-20160831-snap-htmlstory.html) + Transcript of Donald Trump’s speech on national security in Philadelphia - The Hill, 09/07/16 + Transcript of President-elect Trump’s news conference CNBC, 01/11/2017

speech1=paste(readLines("../data/fulltext/SpeechDonaldTrump-NA.txt", 
                  n=-1, skipNul=TRUE),
              collapse=" ")
speech2=paste(readLines("../data/fulltext/SpeechDonaldTrump-NA2.txt", 
                  n=-1, skipNul=TRUE),
              collapse=" ")
speech3=paste(readLines("../data/fulltext/PressDonaldTrump-NA.txt", 
                  n=-1, skipNul=TRUE),
              collapse=" ")
Trump.speeches=data.frame(
  President=rep("Donald J. Trump", 3),
  File=rep("DonaldJTrump", 3),
  Term=rep(0, 3),
  Party=rep("Republican", 3),
  Date=c("August 31, 2016", "September 7, 2016", "January 11, 2017"),
  Words=c(word_count(speech1), word_count(speech2), word_count(speech3)),
  Win=rep("yes", 3),
  type=rep("speeches", 3),
  links=rep(NA, 3),
  urls=rep(NA, 3),
  fulltext=c(speech1, speech2, speech3)
)
speech.list=rbind(speech.list, Trump.speeches)

We seperate the “fulltext” column into seperated sentences and we assign an sequential id to each sentence in a speech (sent.id) and also calculated the number of words in each sentence as sentence length (word.count).

sentence.list=NULL
for(i in 1:nrow(speech.list)){
  sentences=sent_detect(speech.list$fulltext[i],
                        endmarks = c("?", ".", "!", "|",";"))
  if(length(sentences)>0){
    emotions=get_nrc_sentiment(sentences)
    word.count=word_count(sentences)
    # colnames(emotions)=paste0("emo.", colnames(emotions))
    # in case the word counts are zeros?
    emotions=diag(1/(word.count+0.01))%*%as.matrix(emotions)
    sentence.list=rbind(sentence.list, 
                        cbind(speech.list[i,-ncol(speech.list)],
                              sentences=as.character(sentences), 
                              word.count,
                              emotions,
                              sent.id=1:length(sentences)
                              )
    )
  }
}

Some non-sentences exist in raw data due to erroneous extra end-of sentence marks.

sentence.list=
  sentence.list%>%
  filter(!is.na(word.count)) 

part2: Topic modeling

For topic modeling, we prepare a corpus of sentence snipets as follows. For each speech, we start with sentences and prepare a snipet with a given sentence with the flanking sentences.

corpus.list=sentence.list[2:(nrow(sentence.list)-1), ]
sentence.pre=sentence.list$sentences[1:(nrow(sentence.list)-2)]
sentence.post=sentence.list$sentences[3:(nrow(sentence.list)-1)]
corpus.list$snipets=paste(sentence.pre, corpus.list$sentences, sentence.post, sep=" ")
rm.rows=(1:nrow(corpus.list))[corpus.list$sent.id==1]
rm.rows=c(rm.rows, rm.rows-1)
corpus.list=corpus.list[-rm.rows, ]

Text mining

docs <- Corpus(VectorSource(corpus.list$snipets))
writeLines(as.character(docs[[sample(1:nrow(corpus.list), 1)]]))
Each moment in history is a fleeting time, precious and unique. But some stand out as moments of beginning, in which courses are set that shape decades or centuries. This can be such a moment.

Text basic processing

Adapted from https://eight2late.wordpress.com/2015/09/29/a-gentle-introduction-to-topic-modeling-using-r/.

#remove potentially problematic symbols
#transform upper letter to lower letter
docs <-tm_map(docs,content_transformer(tolower)) 
writeLines(as.character(docs[[sample(1:nrow(corpus.list), 1)]]))
not only those who are now making their tax returns, but those who meet the enhanced cost of existence in their monthly bills, know by hard experience what this great burden is and what it does. no matter what others may want, these people want a drastic economy. they are opposed to waste.
#remove punctuation
docs <- tm_map(docs, removePunctuation)
writeLines(as.character(docs[[sample(1:nrow(corpus.list), 1)]]))
let us then fellowcitizens unite with one heart and one mind let us restore to social intercourse that harmony and affection without which liberty and even life itself are but dreary things and let us reflect that having banished from our land that religious intolerance under which mankind so long bled and suffered we have yet gained little if we countenance a political intolerance as despotic as wicked and capable of as bitter and bloody persecutions
#Strip digits
docs <- tm_map(docs, removeNumbers)
writeLines(as.character(docs[[sample(1:nrow(corpus.list), 1)]]))
this wont be a rally speech per se instead im going to deliver a detailed policy address on one of the greatest challenges facing our country today illegal immigration ive just landed having returned from a very important and special meeting with the president of mexico a man i like and respect very much
#remove stopwords
docs <- tm_map(docs, removeWords, stopwords("english"))
writeLines(as.character(docs[[sample(1:nrow(corpus.list), 1)]]))
 people   united states   failed   need   registered  mandate   want direct vigorous action   asked  discipline  direction  leadership
#remove whitespace
docs <- tm_map(docs, stripWhitespace)
writeLines(as.character(docs[[sample(1:nrow(corpus.list), 1)]]))
 will america uncrossed desert unclimbed ridge
#Stem document
#worked working work are the same meaning, this is what this step is doing 
docs <- tm_map(docs,stemDocument)
writeLines(as.character(docs[[sample(1:nrow(corpus.list), 1)]]))
sustain educ advanc knowledg growth religi spirit toler faith strengthen home

Topic modeling

Gengerate document-term matrices.

dtm <- DocumentTermMatrix(docs)
#convert rownames to filenames#convert rownames to filenames
rownames(dtm) <- paste(corpus.list$type, corpus.list$File,
                       corpus.list$Term, corpus.list$sent.id, sep="_")
rowTotals <- apply(dtm , 1, sum) #Find the sum of words in each Document
dtm  <- dtm[rowTotals> 0, ]
corpus.list=corpus.list[rowTotals>0, ]

Run LDA

#Set parameters for Gibbs sampling
burnin <- 4000 # numbers we do not need 
iter <- 2000
thin <- 500 
seed <-list(2003,5,63,100001,765) # seed to start mcmc, reproducible
nstart <- 5
best <- TRUE
#Number of topics
k <- 20 #number of topics in anaylsis(may be a guess)
#Run LDA using Gibbs sampling
ldaOut <-LDA(dtm, k, method="Gibbs", control=list(nstart=nstart, 
                                                 seed = seed, best=best,
                                                 burnin = burnin, iter = iter, 
                                                 thin=thin))
#write out results
#docs to topics
ldaOut.topics <- as.matrix(topics(ldaOut))
table(c(1:k, ldaOut.topics))

  1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20 
319 783 313 292 280 378 483 472 307 297 399 304 413 211 305 315 251 210 199 245 
write.csv(ldaOut.topics,file=paste("../out/LDAGibbs",k,"DocsToTopics.csv"))
#top 6 terms in each topic
ldaOut.terms <- as.matrix(terms(ldaOut,20))
write.csv(ldaOut.terms,file=paste("../out/LDAGibbs",k,"TopicsToTerms.csv"))
#probabilities associated with each topic assignment
topicProbabilities <- as.data.frame(ldaOut@gamma)
write.csv(topicProbabilities,file=paste("../out/LDAGibbs",k,"TopicProbabilities.csv"))
terms.beta=ldaOut@beta
terms.beta=scale(terms.beta)
topics.terms=NULL
for(i in 1:k){
  topics.terms=rbind(topics.terms, ldaOut@terms[order(terms.beta[i,], decreasing = TRUE)[1:7]])
}
topics.terms
      [,1]        [,2]        [,3]         [,4]          [,5]           [,6]         [,7]         
 [1,] "deleg"     "concentr"  "junctur"    "overflow"    "forthsometim" "unadjust"   "imbib"      
 [2,] "happen"    "realli"    "russia"     "big"         "didnt"        "got"        "there"      
 [3,] "construct" "naval"     "rapid"      "canal"       "fortif"       "correspond" "interior"   
 [4,] "aim"       "woe"       "prerog"     "cancer"      "waver"        "token"      "solitari"   
 [5,] "fals"      "bosom"     "profess"    "genuin"      "templ"        "inher"      "aristocraci"
 [6,] "thrive"    "asham"     "infirm"     "exil"        "comput"       "shift"      "shutter"    
 [7,] "communism" "proven"    "hunger"     "unselfish"   "ideolog"      "steadfast"  "beneath"    
 [8,] "dream"     "truli"     "willing"    "bright"      "imag"         "edg"        "depth"      
 [9,] "separ"     "texa"      "endang"     "speedili"    "dissolut"     "confin"     "thirteen"   
[10,] "promin"    "incompet"  "dilig"      "civilservic" "unreserv"     "falsehood"  "unaid"      
[11,] "immigr"    "border"    "clinton"    "crimin"      "alien"        "terror"     "–"          
[12,] "celebr"    "planet"    "bush"       "weari"       "recit"        "miss"       "fleet"      
[13,] "industri"  "revenu"    "expenditur" "tariff"      "surplus"      "farm"       "contract"   
[14,] "prefer"    "purchas"   "equit"      "pretext"     "discern"      "worship"    "idl"        
[15,] "gratitud"  "divin"     "ardent"     "sensibl"     "vicissitud"   "flatter"    "emot"       
[16,] "jurisdict" "urg"       "obey"       "judiciari"   "veto"         "railway"    "supremaci"  
[17,] "technic"   "inexhaust" "accid"      "tumult"      "insidi"       "symptom"    "comiti"     
[18,] "south"     "suffrag"   "southern"   "color"       "voter"        "perish"     "further"    
[19,] "stake"     "cure"      "bar"        "rage"        "savag"        "dissent"    "dissatisfi" 
[20,] "neutral"   "attitud"   "britain"    "undisturb"   "diplomat"     "inflex"     "faroff"     
ldaOut.terms
      Topic 1      Topic 2  Topic 3     Topic 4      Topic 5   Topic 6    Topic 7     Topic 8   
 [1,] "govern"     "want"   "war"       "will"       "spirit"  "new"      "world"     "america" 
 [2,] "power"      "mani"   "great"     "make"       "liberti" "work"     "freedom"   "let"     
 [3,] "general"    "know"   "made"      "now"        "peopl"   "live"     "peac"      "american"
 [4,] "limit"      "say"    "forc"      "good"       "much"    "american" "free"      "togeth"  
 [5,] "feder"      "think"  "maintain"  "believ"     "mean"    "old"      "human"     "god"     
 [6,] "peopl"      "thing"  "necessari" "futur"      "life"    "home"     "men"       "faith"   
 [7,] "within"     "dont"   "resourc"   "past"       "still"   "better"   "seek"      "generat" 
 [8,] "upon"       "back"   "might"     "lead"       "success" "must"     "help"      "heart"   
 [9,] "system"     "like"   "may"       "bring"      "everi"   "find"     "know"      "hope"    
[10,] "experi"     "just"   "commerc"   "understand" "prosper" "land"     "nation"    "end"     
[11,] "exercis"    "get"    "progress"  "follow"     "republ"  "opportun" "strength"  "word"    
[12,] "instrument" "great"  "armi"      "greatest"   "continu" "children" "hope"      "call"    
[13,] "object"     "way"    "proper"    "requir"     "yet"     "need"     "man"       "courag"  
[14,] "concern"    "look"   "prepar"    "sacrific"   "caus"    "way"      "long"      "dream"   
[15,] "grant"      "happen" "import"    "hard"       "true"    "make"     "democraci" "today"   
[16,] "observ"     "theyr"  "improv"    "effort"     "love"    "everi"    "forc"      "voic"    
[17,] "establish"  "meet"   "defens"    "rather"     "chang"   "respons"  "mankind"   "promis"  
[18,] "respect"    "lot"    "without"   "save"       "done"    "reach"    "achiev"    "bless"   
[19,] "author"     "thank"  "civil"     "possibl"    "remain"  "famili"   "progress"  "across"  
[20,] "direct"     "deal"   "militari"  "step"       "fail"    "see"      "earth"     "forward" 
      Topic 9     Topic 10   Topic 11   Topic 12  Topic 13     Topic 14   Topic 15        Topic 16   
 [1,] "state"     "shall"    "countri"  "time"    "industri"   "right"    "countri"       "law"      
 [2,] "unit"      "duti"     "immigr"   "year"    "public"     "citizen"  "may"           "constitut"
 [3,] "union"     "public"   "also"     "day"     "busi"       "upon"     "confid"        "congress" 
 [4,] "constitut" "offic"    "take"     "presid"  "product"    "equal"    "honor"         "execut"   
 [5,] "whole"     "servic"   "million"  "now"     "revenu"     "everi"    "patriot"       "may"      
 [6,] "territori" "respons"  "number"   "first"   "may"        "just"     "fellowcitizen" "legisl"   
 [7,] "form"      "best"     "defens"   "today"   "demand"     "secur"    "circumst"      "elect"    
 [8,] "exist"     "call"     "illeg"    "come"    "tax"        "individu" "high"          "upon"     
 [9,] "institut"  "faith"    "border"   "centuri" "economi"    "principl" "whose"         "effect"   
[10,] "extend"    "oblig"    "mani"     "histori" "secur"      "well"     "happi"         "enforc"   
[11,] "part"      "execut"   "plan"     "stand"   "money"      "act"      "experi"        "author"   
[12,] "preserv"   "support"  "system"   "chang"   "condit"     "found"    "conduct"       "pass"     
[13,] "domest"    "principl" "hillari"  "moment"  "protect"    "support"  "exampl"        "case"     
[14,] "import"    "give"     "clinton"  "last"    "expenditur" "protect"  "great"         "rule"     
[15,] "object"    "civil"    "american" "ago"     "practic"    "rest"     "occas"         "decis"    
[16,] "independ"  "upon"     "ask"      "begin"   "trade"      "whether"  "intellig"      "subject"  
[17,] "term"      "trust"    "open"     "fellow"  "pay"        "other"    "devot"         "depart"   
[18,] "popul"     "purpos"   "crimin"   "oath"    "increas"    "fair"     "express"       "major"    
[19,] "perfect"   "pledg"    "remov"    "sinc"    "labor"      "enjoy"    "degre"         "recommend"
[20,] "consequ"   "perform"  "includ"   "justic"  "debt"       "privileg" "everi"         "amend"    
      Topic 17   Topic 18   Topic 19  Topic 20  
 [1,] "must"     "one"      "can"     "nation"  
 [2,] "peopl"    "countri"  "peopl"   "polici"  
 [3,] "need"     "parti"    "never"   "peac"    
 [4,] "use"      "polit"    "nation"  "interest"
 [5,] "nation"   "question" "great"   "foreign" 
 [6,] "purpos"   "differ"   "hand"    "respect" 
 [7,] "common"   "anoth"    "without" "relat"   
 [8,] "order"    "interest" "strong"  "countri" 
 [9,] "problem"  "good"     "ever"    "among"   
[10,] "moral"    "member"   "place"   "cours"   
[11,] "justic"   "well"     "even"    "intern"  
[12,] "task"     "opinion"  "fear"    "friend"  
[13,] "knowledg" "feel"     "alway"   "desir"   
[14,] "high"     "race"     "less"    "honor"   
[15,] "sens"     "mere"     "reason"  "justic"  
[16,] "among"    "may"      "present" "determin"
[17,] "econom"   "can"      "side"    "wish"    
[18,] "develop"  "seem"     "natur"   "promot"  
[19,] "hold"     "section"  "littl"   "independ"
[20,] "realiz"   "south"    "weak"    "best"    

Based on the most popular terms and the most salient terms for each topic, we assign a hashtag to each topic. It is easy to find that the topic 13 contains so many words about economy, such as tax, debt, pay, etc. And the topic 3 contains many words about military, such as war, force, military, etc. So we can definitely define the topic of 13 and 3 are economy and military, and then we use the first common words of other topics as their hashtag. And this is what we do next.

topics.hash=as.vector(ldaOut.terms[1, ])
topics.hash[13] <- "economy"
topics.hash[15] <- "lucky"
topics.hash[3] <- "military"
corpus.list$ldatopic=as.vector(ldaOut.topics)
corpus.list$ldahash=topics.hash[ldaOut.topics]
colnames(topicProbabilities)=topics.hash
corpus.list.df=cbind(corpus.list, topicProbabilities)

Clustering of topics

par(mar=c(1,1,1,1))
topic.summary=tbl_df(corpus.list.df)%>%
              select(File, govern:nation)%>%
              group_by(File)%>%
              summarise_each(funs(mean))
`summarise_each()` is deprecated.
Use `summarise_all()`, `summarise_at()` or `summarise_if()` instead.
To map `funs` over all variables, use `summarise_all()`
topic.summary=as.data.frame(topic.summary)
rownames(topic.summary)=topic.summary[,1]
topic.plot=c(1, 13, 9, 11, 8, 3, 7)
print(topics.hash[topic.plot])
[1] "govern"   "economy"  "state"    "countri"  "america"  "military" "world"   
heatmap.2(as.matrix(topic.summary[,topic.plot+1]), 
          scale = "column", key=F, 
          col = bluered(100),
          cexRow = 0.9, cexCol = 0.9, margins = c(8, 8),
          trace = "none", density.info = "none")

Now we take five recent presidents to make this question simpler. The most recent 5 presidents are GeorgeBush, WilliamJClinton, GeorgeWBush, BarackObama, DonaldJTrump. Now we make a plot to see which topic they pay attention to more.

par(mfrow=c(5, 1), mar=c(1,1,2,0), bty="n", xaxt="n", yaxt="n")
topic.plot=c(3, 13, 14, 15, 8, 9, 12)
print(topics.hash[topic.plot])
[1] "military" "economy"  "right"    "lucky"    "america"  "state"    "time"    
speech.df=tbl_df(corpus.list.df)%>%filter(File=="GeorgeBush", type=="inaug", Term==1)%>%select(sent.id, govern:nation)
speech.df=as.matrix(speech.df)
speech.df[,-1]=replace(speech.df[,-1], speech.df[,-1]<1/15, 0.001)
speech.df[,-1]=f.smooth.topic(x=speech.df[,1], y=speech.df[,-1])
plot.stacked(speech.df[,1], speech.df[,topic.plot+1],
             xlab="Sentences", ylab="Topic share", main="George Bush, inaugural Speeches")
[1] 0.04472720 0.08945439 0.13418159 0.17890878 0.22363598 0.26836318 0.31309037
speech.df=tbl_df(corpus.list.df)%>%filter(File=="WilliamJClinton", type=="inaug", Term==1)%>%select(sent.id, govern:nation)
speech.df=as.matrix(speech.df)
speech.df[,-1]=replace(speech.df[,-1], speech.df[,-1]<1/15, 0.001)
speech.df[,-1]=f.smooth.topic(x=speech.df[,1], y=speech.df[,-1])
plot.stacked(speech.df[,1], speech.df[,topic.plot+1],
             xlab="Sentences", ylab="Topic share", main="William J Clinton, inaugural Speeches")
[1] 0.04653387 0.09306773 0.13960160 0.18613547 0.23266933 0.27920320 0.32573706
speech.df=tbl_df(corpus.list.df)%>%filter(File=="GeorgeWBush", type=="inaug", Term==1)%>%select(sent.id, govern:nation)
speech.df=as.matrix(speech.df)
speech.df[,-1]=replace(speech.df[,-1], speech.df[,-1]<1/15, 0.001)
speech.df[,-1]=f.smooth.topic(x=speech.df[,1], y=speech.df[,-1])
plot.stacked(speech.df[,1], speech.df[,topic.plot+1],
             xlab="Sentences", ylab="Topic share", main="George W. Bush, inaugural Speeches")
[1] 0.04136933 0.08273866 0.12410799 0.16547732 0.20684666 0.24821599 0.28958532
speech.df=tbl_df(corpus.list.df)%>%filter(File=="BarackObama", type=="inaug", Term==1)%>%select(sent.id, govern:nation)
speech.df=as.matrix(speech.df)
speech.df[,-1]=replace(speech.df[,-1], speech.df[,-1]<1/15, 0.001)
speech.df[,-1]=f.smooth.topic(x=speech.df[,1], y=speech.df[,-1])
plot.stacked(speech.df[,1], speech.df[,topic.plot+1],
             xlab="Sentences", ylab="Topic share", main="Barack Obama, inaugural Speeches")
[1] 0.04448455 0.08896909 0.13345364 0.17793819 0.22242274 0.26690728 0.31139183
speech.df=tbl_df(corpus.list.df)%>%filter(File=="DonaldJTrump", type=="inaug", Term==1)%>%select(sent.id, govern:nation)
speech.df=as.matrix(speech.df)
speech.df[,-1]=replace(speech.df[,-1], speech.df[,-1]<1/15, 0.001)
speech.df[,-1]=f.smooth.topic(x=speech.df[,1], y=speech.df[,-1])
plot.stacked(speech.df[,1], speech.df[,topic.plot+1],
             xlab="Sentences", ylab="Topic share", main="Donald J. Trump, inaugural Speeches")
[1] 0.04306823 0.08613646 0.12920469 0.17227292 0.21534115 0.25840938 0.30147761

From the plot, we can find that Barack Obama’s fist inaugural topic contains much more “economy” than other presidents, but why? As we know, Global financial crisis happend in 2008, and Obama became the president in 2009. So he needed to take some economical strategies to revive American’s economy. Someone may ask that Barack Obama talked more about economy beacause he prefered to pay attention to American economy, but not because the 2008 Global financial crisis happened. We can make a further plot to compare the 1st term inaugural speech and 2nd term inaugural speech of Barack Obama to see the specific reason.

par(mfrow=c(2, 1), mar=c(1,1,2,0), bty="n", xaxt="n", yaxt="n")
speech.df=tbl_df(corpus.list.df)%>%filter(File=="BarackObama", type=="inaug", Term==1)%>%select(sent.id, govern:nation)
speech.df=as.matrix(speech.df)
speech.df[,-1]=replace(speech.df[,-1], speech.df[,-1]<1/15, 0.001)
speech.df[,-1]=f.smooth.topic(x=speech.df[,1], y=speech.df[,-1])
plot.stacked(speech.df[,1], speech.df[,topic.plot+1],
             xlab="Sentences", ylab="Topic share", main="Barack Obama, inaugural Speeches, 1st term")
[1] 0.04448455 0.08896909 0.13345364 0.17793819 0.22242274 0.26690728 0.31139183
speech.df=tbl_df(corpus.list.df)%>%filter(File=="BarackObama", type=="inaug", Term==2)%>%select(sent.id, govern:nation)
speech.df=as.matrix(speech.df)
speech.df[,-1]=replace(speech.df[,-1], speech.df[,-1]<1/15, 0.001)
speech.df[,-1]=f.smooth.topic(x=speech.df[,1], y=speech.df[,-1])
plot.stacked(speech.df[,1], speech.df[,topic.plot+1],
             xlab="Sentences", ylab="Topic share", main="Barack Obama, inaugural Speeches, 2nd term")
[1] 0.03867174 0.07734348 0.11601523 0.15468697 0.19335871 0.23203045 0.27070220

From this plot, we find that compared to the first inaugural speech, Barack Obama talked much less about economy. So we know that the reason Barack Obama talked much about economy is that he needed to take some positive economy strategy in response of the global financial crisis. So, In other words, can we say that some big events can affect the topic of presidents’ inaugural speech? Now, let’s make a graph compared the first and second inaugural speech of GeorgeWBush.

par(mfrow=c(2, 1), mar=c(1,1,2,0), bty="n", xaxt="n", yaxt="n")
speech.df=tbl_df(corpus.list.df)%>%filter(File=="GeorgeWBush", type=="inaug", Term==1)%>%select(sent.id, govern:nation)
speech.df=as.matrix(speech.df)
speech.df[,-1]=replace(speech.df[,-1], speech.df[,-1]<1/15, 0.001)
speech.df[,-1]=f.smooth.topic(x=speech.df[,1], y=speech.df[,-1])
plot.stacked(speech.df[,1], speech.df[,topic.plot+1],
             xlab="Sentences", ylab="Topic share", main="George W. Bush, inaugural Speeches")
[1] 0.04136933 0.08273866 0.12410799 0.16547732 0.20684666 0.24821599 0.28958532
speech.df=tbl_df(corpus.list.df)%>%filter(File=="GeorgeWBush", type=="inaug", Term==2)%>%select(sent.id, govern:nation)
speech.df=as.matrix(speech.df)
speech.df[,-1]=replace(speech.df[,-1], speech.df[,-1]<1/15, 0.001)
speech.df[,-1]=f.smooth.topic(x=speech.df[,1], y=speech.df[,-1])
plot.stacked(speech.df[,1], speech.df[,topic.plot+1],
             xlab="Sentences", ylab="Topic share", main="George W. Bush, inaugural Speeches")
[1] 0.06058238 0.12116475 0.18174713 0.24232951 0.30291189 0.36349426 0.42407664

From this graph, we can find GeorgeWBush talked much more about military at the beginning of his topic. As we know, “September 11 attacks” happened in 2001 when GeorgeWBush was being the president of his first term. So if he wanted to be the president for the second term, he must say something about solving terrorist attack. So we can hold the opinion that big events can affect the inaugural speeches of presidents more or less.

Now we make five clusters to classify inaugural speeches of all US presidents.

presid.summary=tbl_df(corpus.list.df)%>%
  filter(type=="inaug")%>%
  select(File, govern:nation)%>%
  group_by(File)%>%
  summarise_each(funs(mean))
`summarise_each()` is deprecated.
Use `summarise_all()`, `summarise_at()` or `summarise_if()` instead.
To map `funs` over all variables, use `summarise_all()`
presid.summary=as.data.frame(presid.summary)
rownames(presid.summary)=as.character((presid.summary[,1]))
km.res=kmeans(scale(presid.summary[,-1]), iter.max=200,
              5)
fviz_cluster(km.res, 
             stand=T, repel= TRUE,
             data = presid.summary[,-1],
             show.clust.cent=FALSE)

From this graph, we find that the five most recent presidents, GeorgeBush, WilliamJClinton, GeorgeWBush, BarackObama, DonaldJTrump are in the left cluster of the graph above. It seems that different era will lead to different topic(in other words, they may face the same problem during the same era.)

part3:word cloud

First, let make a word cloud to show the most common 50 words of five presidents (GeorgeBush, WilliamJClinton, GeorgeWBush, BarackObama, DonaldJTrump) ’inaugural speeches.

td1 <- sentence.list %>% filter(File %in% c("BarackObama", "DonaldJTrump", "GeorgeWBush", "WilliamJClinton", "GeorgeBush"), Term == 1) %>% select(File, sentences) 
td1$sentences <- as.character(td1$sentences)
td1 <- td1 %>% unnest_tokens(word, sentences)
td1 <- td1 %>%
  anti_join(stop_words, by = "word")
td1 %>%
  count(word) %>%
  with(wordcloud(word, n, max.words = 50, color = c("purple4", "red4", "black")))

We can find the top 8 most common words are america, world, nation, americans, people, americans, american, time, country, people. It is obvious that the inaugural speeches should concentrate on what they can achieve for “America”.

Now we have a look at which president like to use these common words most.

foo <- td1 %>%
  group_by(word, File) %>%
  count()
bar <- td1 %>%
  group_by(word) %>%
  count() %>%
  rename(all = n)
foo %>%
  left_join(bar, by = "word") %>%
  arrange(desc(all)) %>%
  head(80) %>%
  ungroup() %>%
  ggplot(aes(reorder(word, all, FUN = min), n, fill = File)) +
  #ggplot(aes(word, n)) +
  geom_col() +
  xlab(NULL) +
  coord_flip() +
  facet_wrap(~ File) +
  theme(legend.position = "none")

Barack Obama used “nation” most, DonaldJTrump used “american” most, and WilliamJClinton used “world” most.

We can dive deeper into this fundamental question by directly comparing the relative frequency of word use between presidents. In this example, we will compare WilliamJClinton to the other presidents.

frequency1 <- td1 %>%
  count(File, word) %>%
  group_by(File) %>%
  mutate(freq = n / sum(n)) %>% 
  select(-n) %>% 
  spread(File, freq) %>% 
  gather(File, freq, BarackObama : GeorgeWBush) %>%
  filter(!is.na(freq) & !is.na(File))
ggplot(frequency1, aes(freq, WilliamJClinton , color = abs(WilliamJClinton - freq))) +
  geom_abline(color = "gray40", lty = 2) +
  geom_jitter(alpha = 0.1, size = 2.5, width = 0.1, height = 0.1) +
  geom_text(aes(label = word), check_overlap = TRUE, vjust = 1.5) +
  scale_x_log10(labels = percent_format()) +
  scale_y_log10(labels = percent_format()) +
  #scale_color_gradient(limits = c(0, 0.001), low = "darkslategray4", high = "gray95") +
  facet_wrap(~File, ncol = 2) +
  theme(legend.position="none") +
  labs(y = "WilliamJClintonp", x = NULL)

In these plots, words that are close to the dashed line (of equal frequency) have similar frequencies in the corresponding presidents. Words that are further along a particular president axis (such as “change” for William J Clinton vs Barack Obama) are more frequent for that president. The blue-gray scale indicates how different the WilliamJClinton frequency is from the overall frequency (with higher relative frequencies being lighter). The (slightly jittered) points in the background represent the complete set of (high-frequency) words, whereas the displayed words have been chosen to avoid overlap.

The plots give us another useful overview angle. For instance, they suggest that WIlliamJClinton uses “fellow” more than other four presidents, but he uses “americans” as more often than Barack Obama and George Bush but equally often as Donald Trump and George W Bush.

part4:sentiment analysis

td1 %>%
  filter(File == "BarackObama") %>%
  inner_join(get_sentiments("bing"), by = "word") %>%
  count(word, sentiment, sort = TRUE) %>%
  ungroup() %>%
  group_by(sentiment) %>%
  top_n(10, n) %>%
  ungroup() %>%
  mutate(word = reorder(word, n)) %>%
  ggplot(aes(word, n, fill = sentiment)) +
  geom_col(show.legend = FALSE) +
  facet_wrap(~sentiment, scales = "free_y") +
  labs(y = "Contribution to negative/positive sentiment", x = NULL) +
  coord_flip() +
  ggtitle("Barack Obama - Sentiment analysis")

td1 %>%
  filter(File == "DonaldJTrump") %>%
  inner_join(get_sentiments("bing"), by = "word") %>%
  count(word, sentiment, sort = TRUE) %>%
  ungroup() %>%
  group_by(sentiment) %>%
  top_n(10, n) %>%
  ungroup() %>%
  mutate(word = reorder(word, n)) %>%
  ggplot(aes(word, n, fill = sentiment)) +
  geom_col(show.legend = FALSE) +
  facet_wrap(~sentiment, scales = "free_y") +
  labs(y = "Contribution to negative/positive sentiment", x = NULL) +
  coord_flip() +
  ggtitle("DonaldJTrump - Sentiment analysis")

Comparison wordcloud

An alternative way of portraying the most important words for each sentiment is through our favourite word-clouds. This uses the reshape library. Here are all the presidents combined as an example:

td1 %>%
  inner_join(get_sentiments("bing"), by = "word") %>%
  count(word, sentiment, sort = TRUE) %>%
  acast(word ~ sentiment, value.var = "n", fill = 0) %>%
  comparison.cloud(colors = c("#F8766D", "#00BFC4"), max.words = 50)

We can see “hard” and “freedom” squaring up against one another; locked in an eternal battle in which “success” counters “gail” and “unity” rages against “break”.

LS0tCnRpdGxlOiAiTXkgTm90ZWJvb2siCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgZGZfcHJpbnQ6IHBhZ2VkCi0tLQoKCiMgY2hlY2sgYW5kIGluc3RhbGwgbmVlZGVkIHBhY2thZ2VzLiBMb2FkIHRoZSBsaWJyYXJpZXMgYW5kIGZ1bmN0aW9ucy4gCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KcGFja2FnZXMudXNlZD1jKCJydmVzdCIsICJ0aWJibGUiLCAicWRhcCIsIAogICAgICAgICAgICAgICAgInNlbnRpbWVudHIiLCAiZ3Bsb3RzIiwgImRwbHlyIiwKICAgICAgICAgICAgICAgICJ0bSIsICJzeXV6aGV0IiwgImZhY3RvZXh0cmEiLCAKICAgICAgICAgICAgICAgICJiZWVzd2FybSIsICJzY2FsZXMiLCAiUkNvbG9yQnJld2VyIiwKICAgICAgICAgICAgICAgICJSQU5OIiwgInRtIiwgInRvcGljbW9kZWxzIikKCiMgY2hlY2sgcGFja2FnZXMgdGhhdCBuZWVkIHRvIGJlIGluc3RhbGxlZC4KcGFja2FnZXMubmVlZGVkPXNldGRpZmYocGFja2FnZXMudXNlZCwgCiAgICAgICAgICAgICAgICAgICAgICAgIGludGVyc2VjdChpbnN0YWxsZWQucGFja2FnZXMoKVssMV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFja2FnZXMudXNlZCkpCiMgaW5zdGFsbCBhZGRpdGlvbmFsIHBhY2thZ2VzCmlmKGxlbmd0aChwYWNrYWdlcy5uZWVkZWQpPjApewogIGluc3RhbGwucGFja2FnZXMocGFja2FnZXMubmVlZGVkLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQp9CgojIGxvYWQgcGFja2FnZXMKbGlicmFyeSgicnZlc3QiKQpsaWJyYXJ5KCJ0aWJibGUiKQpsaWJyYXJ5KCJxZGFwIikKbGlicmFyeSgic2VudGltZW50ciIpCmxpYnJhcnkoImdwbG90cyIpCmxpYnJhcnkoImRwbHlyIikKbGlicmFyeSgidG0iKQpsaWJyYXJ5KCJzeXV6aGV0IikKbGlicmFyeSgiZmFjdG9leHRyYSIpCmxpYnJhcnkoImJlZXN3YXJtIikKbGlicmFyeSgic2NhbGVzIikKbGlicmFyeSgiUkNvbG9yQnJld2VyIikKbGlicmFyeSgiUkFOTiIpCmxpYnJhcnkoInRtIikKbGlicmFyeSgidG9waWNtb2RlbHMiKQoKc291cmNlKCIuLi9saWIvcGxvdHN0YWNrZWQuUiIpCnNvdXJjZSgiLi4vbGliL3NwZWVjaEZ1bmNzLlIiKQpgYGAKClNjcmFwIHNwZWVjaCBVUkxzIGZyb20gPGh0dHA6Ly93d3cucHJlc2lkZW5jeS51Y3NiLmVkdS8+LgoKIyMjIyBwYXJ0MTpkYXRhIHByZXByb2Nlc3NpbmcgIyMjIwpXaGF0IHdlIGRvIHRoZSBmaXJzdCBwYXJ0IGlzIHRvIGdyYWIgZGF0YSBhbmQgdGhlbiBjb21iaW5lIHRoZSBkYXRhIGludG8gb25lIGRhdGEuZnJhbWUgZm9yIGZ1dHVyZSB1c2UuCgpGb2xsb3dpbmcgdGhlIGV4YW1wbGUgb2YgW0plcmlkIEZyYW5jb21dKGh0dHA6Ly9mcmFuY29qYy5naXRodWIuaW8vd2ViLXNjcmFwaW5nLXdpdGgtcnZlc3QvKSwgd2UgdXNlZCBbU2VsZWN0b3JnYWRnZXRdKGh0dHA6Ly9zZWxlY3RvcmdhZGdldC5jb20vKSB0byBjaG9vc2UgdGhlIGxpbmtzIHdlIHdvdWxkIGxpa2UgdG8gc2NyYXAuIEZvciB0aGlzIHByb2plY3QsIHdlIHNlbGVjdGVkIGFsbCBpbmF1Z3VyYWwgYWRkcmVzc2VzIG9mIHBhc3QgcHJlc2lkZW50cywgbm9taW5hdGlvbiBzcGVlY2hlcyBvZiBtYWpvciBwYXJ0eSBjYW5kaWRhdGVzIGFuZCBmYXJld2VsbCBhZGRyZXNzZXMuIFdlIGFsc28gaW5jbHVkZWQgc2V2ZXJhbCBwdWJsaWMgc3BlZWNoZXMgZnJvbSBEb25hbGQgVHJ1bXAgZm9yIG91ciB0ZXh0dWFsIGFuYWx5c2lzIG9mIHByZXNpZGVudGlhbCBzcGVlY2hlcy4gCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyMjIEluYXVndWFyYWwgc3BlZWNoZXMKbWFpbi5wYWdlIDwtIHJlYWRfaHRtbCh4ID0gImh0dHA6Ly93d3cucHJlc2lkZW5jeS51Y3NiLmVkdS9pbmF1Z3VyYWxzLnBocCIpCiMgR2V0IGxpbmsgVVJMcwojIGYuc3BlZWNobGlua3MgaXMgYSBmdW5jdGlvbiBmb3IgZXh0cmFjdGluZyBsaW5rcyBmcm9tIHRoZSBsaXN0IG9mIHNwZWVjaGVzLiAKaW5hdWc9Zi5zcGVlY2hsaW5rcyhtYWluLnBhZ2UpCiNoZWFkKGluYXVnKQphcy5EYXRlKGluYXVnWywxXSwgZm9ybWF0PSIlQiAlZSwgJVkiKQppbmF1Zz1pbmF1Z1stbnJvdyhpbmF1ZyksXSAjIHJlbW92ZSB0aGUgbGFzdCBsaW5lLCBpcnJlbGV2YW50IGR1ZSB0byBlcnJvci4KCmBgYAoKUmVhZCBkYXRhIGFuZCB0aGVuIHdlIGFzc2VtYmxlIGFsbCBzY3JhcHBlZCBzcGVlY2hlcyBpbnRvIG9uZSBsaXN0LiAKCmBgYHtyfQppbmF1Zy5saXN0PXJlYWQuY3N2KCIuLi9kYXRhL2luYXVnbGlzdC5jc3YiLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCmBgYAoKV2UgYXNzZW1ibGUgYWxsIHNjcmFwcGVkIHNwZWVjaGVzIGludG8gb25lIGxpc3QuIE5vdGUgaGVyZSB0aGF0IHdlIGRvbid0IGhhdmUgdGhlIGZ1bGwgdGV4dCB5ZXQsIG9ubHkgdGhlIGxpbmtzIHRvIGZ1bGwgdGV4dCB0cmFuc2NyaXB0cy4gCgoKYGBge3J9CnNwZWVjaC5saXN0PWluYXVnLmxpc3QKc3BlZWNoLmxpc3QkdHlwZT1yZXAoImluYXVnIiwgbnJvdyhpbmF1Zy5saXN0KSkKc3BlZWNoLnVybD1pbmF1ZwpzcGVlY2gubGlzdD1jYmluZChzcGVlY2gubGlzdCwgc3BlZWNoLnVybCkKYGBgCgpCYXNlZCBvbiB0aGUgbGlzdCBvZiBzcGVlY2hlcywgd2Ugc2NyYXAgdGhlIG1haW4gdGV4dCBwYXJ0IG9mIHRoZSB0cmFuc2NyaXB0J3MgaHRtbCBwYWdlLiBGb3Igc2ltcGxlIGh0bWwgcGFnZXMgb2YgdGhpcyBraW5kLCAgW1NlbGVjdG9yZ2FkZ2V0XShodHRwOi8vc2VsZWN0b3JnYWRnZXQuY29tLykgaXMgdmVyeSBjb252ZW5pZW50IGZvciBpZGVudGlmeWluZyB0aGUgaHRtbCBub2RlIHRoYXQgYHJ2ZXN0YCBjYW4gdXNlIHRvIHNjcmFwIGl0cyBjb250ZW50LiBGb3IgcmVwcm9kdWNpYmlsaXR5LCB3ZSBhbHNvIHNhdmUgb3VyIHNjcmFwcGVkIHNwZWVjaGVzIGludG8gb3VyIGxvY2FsIGZvbGRlciBhcyBpbmRpdmlkdWFsIHNwZWVjaCBmaWxlcy4gCgpgYGB7cn0KIyBMb29wIG92ZXIgZWFjaCByb3cgaW4gc3BlZWNoLmxpc3QKc3BlZWNoLmxpc3QkZnVsbHRleHQ9TkEKZm9yKGkgaW4gc2VxKG5yb3coc3BlZWNoLmxpc3QpKSkgewogIHRleHQgPC0gcmVhZF9odG1sKHNwZWVjaC5saXN0JHVybHNbaV0pICU+JSAjIGxvYWQgdGhlIHBhZ2UKICAgIGh0bWxfbm9kZXMoIi5kaXNwbGF5dGV4dCIpICU+JSAjIGlzbG9hdGUgdGhlIHRleHQKICAgIGh0bWxfdGV4dCgpICMgZ2V0IHRoZSB0ZXh0CiAgc3BlZWNoLmxpc3QkZnVsbHRleHRbaV09dGV4dAogICMgQ3JlYXRlIHRoZSBmaWxlIG5hbWUKICBmaWxlbmFtZSA8LSBwYXN0ZTAoIi4uL2RhdGEvZnVsbHRleHQvIiwgCiAgICAgICAgICAgICAgICAgICAgIHNwZWVjaC5saXN0JHR5cGVbaV0sCiAgICAgICAgICAgICAgICAgICAgIHNwZWVjaC5saXN0JEZpbGVbaV0sICItIiwgCiAgICAgICAgICAgICAgICAgICAgIHNwZWVjaC5saXN0JFRlcm1baV0sICIudHh0IikKICBzaW5rKGZpbGUgPSBmaWxlbmFtZSkgJT4lICMgb3BlbiBmaWxlIHRvIHdyaXRlIAogIGNhdCh0ZXh0KSAgIyB3cml0ZSB0aGUgZmlsZQogIHNpbmsoKSAjIGNsb3NlIHRoZSBmaWxlCn0KYGBgCgpUcnVtcCwgYXMgcHJlc2lkZW50LWVsZWN0IHRoYXQgaGFzIG5vdCBiZWVuIGEgcG9saXRpY2lhbiwgZG8gbm90IGhhdmUgYSBsb3Qgb2YgZm9ybWFsIHNwZWVjaGVzIHlldC4gRm9yIG91ciB0ZXh0dWFsIGFuYWx5c2lzLCB3ZSBtYW51YWxseSBhZGQgc2V2ZXJhbCBwdWJsaWMgdHJhbnNjcmlwdHMgZnJvbSBUcnVtcDoKKyBbVHJhbnNjcmlwdDogRG9uYWxkIFRydW1wJ3MgZnVsbCBpbW1pZ3JhdGlvbiBzcGVlY2gsIGFubm90YXRlZC4gTEEgVGltZXMsIDA4LzMxLzIwMTZdIChodHRwOi8vd3d3LmxhdGltZXMuY29tL3BvbGl0aWNzL2xhLW5hLXBvbC1kb25hbGQtdHJ1bXAtaW1taWdyYXRpb24tc3BlZWNoLXRyYW5zY3JpcHQtMjAxNjA4MzEtc25hcC1odG1sc3RvcnkuaHRtbCkKKyBbVHJhbnNjcmlwdCBvZiBEb25hbGQgVHJ1bXDigJlzIHNwZWVjaCBvbiBuYXRpb25hbCBzZWN1cml0eSBpbiBQaGlsYWRlbHBoaWEKLSBUaGUgSGlsbCwgMDkvMDcvMTZdKGh0dHA6Ly90aGVoaWxsLmNvbS9ibG9ncy9wdW5kaXRzLWJsb2cvY2FtcGFpZ24vMjk0ODE3LXRyYW5zY3JpcHQtb2YtZG9uYWxkLXRydW1wcy1zcGVlY2gtb24tbmF0aW9uYWwtc2VjdXJpdHktaW4pCisgW1RyYW5zY3JpcHQgb2YgUHJlc2lkZW50LWVsZWN0IFRydW1wJ3MgbmV3cyBjb25mZXJlbmNlCkNOQkMsIDAxLzExLzIwMTddKGh0dHA6Ly93d3cuY25iYy5jb20vMjAxNy8wMS8xMS90cmFuc2NyaXB0LW9mLXByZXNpZGVudC1lbGVjdC1kb25hbGQtai10cnVtcHMtbmV3cy1jb25mZXJlbmNlLmh0bWwpCgpgYGB7cn0Kc3BlZWNoMT1wYXN0ZShyZWFkTGluZXMoIi4uL2RhdGEvZnVsbHRleHQvU3BlZWNoRG9uYWxkVHJ1bXAtTkEudHh0IiwgCiAgICAgICAgICAgICAgICAgIG49LTEsIHNraXBOdWw9VFJVRSksCiAgICAgICAgICAgICAgY29sbGFwc2U9IiAiKQpzcGVlY2gyPXBhc3RlKHJlYWRMaW5lcygiLi4vZGF0YS9mdWxsdGV4dC9TcGVlY2hEb25hbGRUcnVtcC1OQTIudHh0IiwgCiAgICAgICAgICAgICAgICAgIG49LTEsIHNraXBOdWw9VFJVRSksCiAgICAgICAgICAgICAgY29sbGFwc2U9IiAiKQpzcGVlY2gzPXBhc3RlKHJlYWRMaW5lcygiLi4vZGF0YS9mdWxsdGV4dC9QcmVzc0RvbmFsZFRydW1wLU5BLnR4dCIsIAogICAgICAgICAgICAgICAgICBuPS0xLCBza2lwTnVsPVRSVUUpLAogICAgICAgICAgICAgIGNvbGxhcHNlPSIgIikKClRydW1wLnNwZWVjaGVzPWRhdGEuZnJhbWUoCiAgUHJlc2lkZW50PXJlcCgiRG9uYWxkIEouIFRydW1wIiwgMyksCiAgRmlsZT1yZXAoIkRvbmFsZEpUcnVtcCIsIDMpLAogIFRlcm09cmVwKDAsIDMpLAogIFBhcnR5PXJlcCgiUmVwdWJsaWNhbiIsIDMpLAogIERhdGU9YygiQXVndXN0IDMxLCAyMDE2IiwgIlNlcHRlbWJlciA3LCAyMDE2IiwgIkphbnVhcnkgMTEsIDIwMTciKSwKICBXb3Jkcz1jKHdvcmRfY291bnQoc3BlZWNoMSksIHdvcmRfY291bnQoc3BlZWNoMiksIHdvcmRfY291bnQoc3BlZWNoMykpLAogIFdpbj1yZXAoInllcyIsIDMpLAogIHR5cGU9cmVwKCJzcGVlY2hlcyIsIDMpLAogIGxpbmtzPXJlcChOQSwgMyksCiAgdXJscz1yZXAoTkEsIDMpLAogIGZ1bGx0ZXh0PWMoc3BlZWNoMSwgc3BlZWNoMiwgc3BlZWNoMykKKQoKc3BlZWNoLmxpc3Q9cmJpbmQoc3BlZWNoLmxpc3QsIFRydW1wLnNwZWVjaGVzKQpgYGAKCgoKV2Ugc2VwZXJhdGUgdGhlICJmdWxsdGV4dCIgY29sdW1uIGludG8gc2VwZXJhdGVkIHNlbnRlbmNlcyBhbmQgd2UgYXNzaWduIGFuIHNlcXVlbnRpYWwgaWQgdG8gZWFjaCBzZW50ZW5jZSBpbiBhIHNwZWVjaCAoYHNlbnQuaWRgKSBhbmQgYWxzbyBjYWxjdWxhdGVkIHRoZSBudW1iZXIgb2Ygd29yZHMgaW4gZWFjaCBzZW50ZW5jZSBhcyAqc2VudGVuY2UgbGVuZ3RoKiAoYHdvcmQuY291bnRgKS4KCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpzZW50ZW5jZS5saXN0PU5VTEwKZm9yKGkgaW4gMTpucm93KHNwZWVjaC5saXN0KSl7CiAgc2VudGVuY2VzPXNlbnRfZGV0ZWN0KHNwZWVjaC5saXN0JGZ1bGx0ZXh0W2ldLAogICAgICAgICAgICAgICAgICAgICAgICBlbmRtYXJrcyA9IGMoIj8iLCAiLiIsICIhIiwgInwiLCI7IikpCiAgaWYobGVuZ3RoKHNlbnRlbmNlcyk+MCl7CiAgICBlbW90aW9ucz1nZXRfbnJjX3NlbnRpbWVudChzZW50ZW5jZXMpCiAgICB3b3JkLmNvdW50PXdvcmRfY291bnQoc2VudGVuY2VzKQogICAgIyBjb2xuYW1lcyhlbW90aW9ucyk9cGFzdGUwKCJlbW8uIiwgY29sbmFtZXMoZW1vdGlvbnMpKQogICAgIyBpbiBjYXNlIHRoZSB3b3JkIGNvdW50cyBhcmUgemVyb3M/CiAgICBlbW90aW9ucz1kaWFnKDEvKHdvcmQuY291bnQrMC4wMSkpJSolYXMubWF0cml4KGVtb3Rpb25zKQogICAgc2VudGVuY2UubGlzdD1yYmluZChzZW50ZW5jZS5saXN0LCAKICAgICAgICAgICAgICAgICAgICAgICAgY2JpbmQoc3BlZWNoLmxpc3RbaSwtbmNvbChzcGVlY2gubGlzdCldLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZW50ZW5jZXM9YXMuY2hhcmFjdGVyKHNlbnRlbmNlcyksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3b3JkLmNvdW50LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbW90aW9ucywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VudC5pZD0xOmxlbmd0aChzZW50ZW5jZXMpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICkKICB9Cn0KYGBgCgpTb21lIG5vbi1zZW50ZW5jZXMgZXhpc3QgaW4gcmF3IGRhdGEgZHVlIHRvIGVycm9uZW91cyBleHRyYSBlbmQtb2Ygc2VudGVuY2UgbWFya3MuIApgYGB7cn0Kc2VudGVuY2UubGlzdD0KICBzZW50ZW5jZS5saXN0JT4lCiAgZmlsdGVyKCFpcy5uYSh3b3JkLmNvdW50KSkgCgpgYGAKCiMjIyMgcGFydDI6IFRvcGljIG1vZGVsaW5nICMjIyMKCkZvciB0b3BpYyBtb2RlbGluZywgd2UgcHJlcGFyZSBhIGNvcnB1cyBvZiBzZW50ZW5jZSBzbmlwZXRzIGFzIGZvbGxvd3MuIEZvciBlYWNoIHNwZWVjaCwgd2Ugc3RhcnQgd2l0aCBzZW50ZW5jZXMgYW5kIHByZXBhcmUgYSBzbmlwZXQgd2l0aCBhIGdpdmVuIHNlbnRlbmNlIHdpdGggdGhlIGZsYW5raW5nIHNlbnRlbmNlcy4gCgoKYGBge3J9CmNvcnB1cy5saXN0PXNlbnRlbmNlLmxpc3RbMjoobnJvdyhzZW50ZW5jZS5saXN0KS0xKSwgXQpzZW50ZW5jZS5wcmU9c2VudGVuY2UubGlzdCRzZW50ZW5jZXNbMToobnJvdyhzZW50ZW5jZS5saXN0KS0yKV0Kc2VudGVuY2UucG9zdD1zZW50ZW5jZS5saXN0JHNlbnRlbmNlc1szOihucm93KHNlbnRlbmNlLmxpc3QpLTEpXQpjb3JwdXMubGlzdCRzbmlwZXRzPXBhc3RlKHNlbnRlbmNlLnByZSwgY29ycHVzLmxpc3Qkc2VudGVuY2VzLCBzZW50ZW5jZS5wb3N0LCBzZXA9IiAiKQpybS5yb3dzPSgxOm5yb3coY29ycHVzLmxpc3QpKVtjb3JwdXMubGlzdCRzZW50LmlkPT0xXQpybS5yb3dzPWMocm0ucm93cywgcm0ucm93cy0xKQpjb3JwdXMubGlzdD1jb3JwdXMubGlzdFstcm0ucm93cywgXQpgYGAKCiMjIFRleHQgbWluaW5nCmBgYHtyfQpkb2NzIDwtIENvcnB1cyhWZWN0b3JTb3VyY2UoY29ycHVzLmxpc3Qkc25pcGV0cykpCndyaXRlTGluZXMoYXMuY2hhcmFjdGVyKGRvY3NbW3NhbXBsZSgxOm5yb3coY29ycHVzLmxpc3QpLCAxKV1dKSkKYGBgCgojIyMgVGV4dCBiYXNpYyBwcm9jZXNzaW5nCkFkYXB0ZWQgZnJvbSA8aHR0cHM6Ly9laWdodDJsYXRlLndvcmRwcmVzcy5jb20vMjAxNS8wOS8yOS9hLWdlbnRsZS1pbnRyb2R1Y3Rpb24tdG8tdG9waWMtbW9kZWxpbmctdXNpbmctci8+LgoKYGBge3J9CiNyZW1vdmUgcG90ZW50aWFsbHkgcHJvYmxlbWF0aWMgc3ltYm9scwojdHJhbnNmb3JtIHVwcGVyIGxldHRlciB0byBsb3dlciBsZXR0ZXIKZG9jcyA8LXRtX21hcChkb2NzLGNvbnRlbnRfdHJhbnNmb3JtZXIodG9sb3dlcikpIAp3cml0ZUxpbmVzKGFzLmNoYXJhY3Rlcihkb2NzW1tzYW1wbGUoMTpucm93KGNvcnB1cy5saXN0KSwgMSldXSkpCgojcmVtb3ZlIHB1bmN0dWF0aW9uCmRvY3MgPC0gdG1fbWFwKGRvY3MsIHJlbW92ZVB1bmN0dWF0aW9uKQp3cml0ZUxpbmVzKGFzLmNoYXJhY3Rlcihkb2NzW1tzYW1wbGUoMTpucm93KGNvcnB1cy5saXN0KSwgMSldXSkpCgojU3RyaXAgZGlnaXRzCmRvY3MgPC0gdG1fbWFwKGRvY3MsIHJlbW92ZU51bWJlcnMpCndyaXRlTGluZXMoYXMuY2hhcmFjdGVyKGRvY3NbW3NhbXBsZSgxOm5yb3coY29ycHVzLmxpc3QpLCAxKV1dKSkKCiNyZW1vdmUgc3RvcHdvcmRzCmRvY3MgPC0gdG1fbWFwKGRvY3MsIHJlbW92ZVdvcmRzLCBzdG9wd29yZHMoImVuZ2xpc2giKSkKd3JpdGVMaW5lcyhhcy5jaGFyYWN0ZXIoZG9jc1tbc2FtcGxlKDE6bnJvdyhjb3JwdXMubGlzdCksIDEpXV0pKQoKI3JlbW92ZSB3aGl0ZXNwYWNlCmRvY3MgPC0gdG1fbWFwKGRvY3MsIHN0cmlwV2hpdGVzcGFjZSkKd3JpdGVMaW5lcyhhcy5jaGFyYWN0ZXIoZG9jc1tbc2FtcGxlKDE6bnJvdyhjb3JwdXMubGlzdCksIDEpXV0pKQoKI1N0ZW0gZG9jdW1lbnQKI3dvcmtlZCB3b3JraW5nIHdvcmsgYXJlIHRoZSBzYW1lIG1lYW5pbmcsIHRoaXMgaXMgd2hhdCB0aGlzIHN0ZXAgaXMgZG9pbmcgCmRvY3MgPC0gdG1fbWFwKGRvY3Msc3RlbURvY3VtZW50KQp3cml0ZUxpbmVzKGFzLmNoYXJhY3Rlcihkb2NzW1tzYW1wbGUoMTpucm93KGNvcnB1cy5saXN0KSwgMSldXSkpCmBgYAoKIyMjIFRvcGljIG1vZGVsaW5nCgpHZW5nZXJhdGUgZG9jdW1lbnQtdGVybSBtYXRyaWNlcy4gCgpgYGB7cn0KZHRtIDwtIERvY3VtZW50VGVybU1hdHJpeChkb2NzKQojY29udmVydCByb3duYW1lcyB0byBmaWxlbmFtZXMjY29udmVydCByb3duYW1lcyB0byBmaWxlbmFtZXMKcm93bmFtZXMoZHRtKSA8LSBwYXN0ZShjb3JwdXMubGlzdCR0eXBlLCBjb3JwdXMubGlzdCRGaWxlLAogICAgICAgICAgICAgICAgICAgICAgIGNvcnB1cy5saXN0JFRlcm0sIGNvcnB1cy5saXN0JHNlbnQuaWQsIHNlcD0iXyIpCgpyb3dUb3RhbHMgPC0gYXBwbHkoZHRtICwgMSwgc3VtKSAjRmluZCB0aGUgc3VtIG9mIHdvcmRzIGluIGVhY2ggRG9jdW1lbnQKCmR0bSAgPC0gZHRtW3Jvd1RvdGFscz4gMCwgXQpjb3JwdXMubGlzdD1jb3JwdXMubGlzdFtyb3dUb3RhbHM+MCwgXQoKYGBgCgpSdW4gTERBCgpgYGB7cn0KI1NldCBwYXJhbWV0ZXJzIGZvciBHaWJicyBzYW1wbGluZwpidXJuaW4gPC0gNDAwMCAjIG51bWJlcnMgd2UgZG8gbm90IG5lZWQgCml0ZXIgPC0gMjAwMAp0aGluIDwtIDUwMCAKc2VlZCA8LWxpc3QoMjAwMyw1LDYzLDEwMDAwMSw3NjUpICMgc2VlZCB0byBzdGFydCBtY21j77yMIHJlcHJvZHVjaWJsZQpuc3RhcnQgPC0gNQpiZXN0IDwtIFRSVUUKCiNOdW1iZXIgb2YgdG9waWNzCmsgPC0gMjAgI251bWJlciBvZiB0b3BpY3MgaW4gYW5heWxzaXMobWF5IGJlIGEgZ3Vlc3MpCgojUnVuIExEQSB1c2luZyBHaWJicyBzYW1wbGluZwpsZGFPdXQgPC1MREEoZHRtLCBrLCBtZXRob2Q9IkdpYmJzIiwgY29udHJvbD1saXN0KG5zdGFydD1uc3RhcnQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VlZCA9IHNlZWQsIGJlc3Q9YmVzdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ1cm5pbiA9IGJ1cm5pbiwgaXRlciA9IGl0ZXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhpbj10aGluKSkKI3dyaXRlIG91dCByZXN1bHRzCiNkb2NzIHRvIHRvcGljcwpsZGFPdXQudG9waWNzIDwtIGFzLm1hdHJpeCh0b3BpY3MobGRhT3V0KSkKdGFibGUoYygxOmssIGxkYU91dC50b3BpY3MpKQp3cml0ZS5jc3YobGRhT3V0LnRvcGljcyxmaWxlPXBhc3RlKCIuLi9vdXQvTERBR2liYnMiLGssIkRvY3NUb1RvcGljcy5jc3YiKSkKCiN0b3AgNiB0ZXJtcyBpbiBlYWNoIHRvcGljCmxkYU91dC50ZXJtcyA8LSBhcy5tYXRyaXgodGVybXMobGRhT3V0LDIwKSkKd3JpdGUuY3N2KGxkYU91dC50ZXJtcyxmaWxlPXBhc3RlKCIuLi9vdXQvTERBR2liYnMiLGssIlRvcGljc1RvVGVybXMuY3N2IikpCgojcHJvYmFiaWxpdGllcyBhc3NvY2lhdGVkIHdpdGggZWFjaCB0b3BpYyBhc3NpZ25tZW50CnRvcGljUHJvYmFiaWxpdGllcyA8LSBhcy5kYXRhLmZyYW1lKGxkYU91dEBnYW1tYSkKd3JpdGUuY3N2KHRvcGljUHJvYmFiaWxpdGllcyxmaWxlPXBhc3RlKCIuLi9vdXQvTERBR2liYnMiLGssIlRvcGljUHJvYmFiaWxpdGllcy5jc3YiKSkKYGBgCmBgYHtyfQp0ZXJtcy5iZXRhPWxkYU91dEBiZXRhCnRlcm1zLmJldGE9c2NhbGUodGVybXMuYmV0YSkKdG9waWNzLnRlcm1zPU5VTEwKZm9yKGkgaW4gMTprKXsKICB0b3BpY3MudGVybXM9cmJpbmQodG9waWNzLnRlcm1zLCBsZGFPdXRAdGVybXNbb3JkZXIodGVybXMuYmV0YVtpLF0sIGRlY3JlYXNpbmcgPSBUUlVFKVsxOjddXSkKfQp0b3BpY3MudGVybXMKbGRhT3V0LnRlcm1zCmBgYAoKQmFzZWQgb24gdGhlIG1vc3QgcG9wdWxhciB0ZXJtcyBhbmQgdGhlIG1vc3Qgc2FsaWVudCB0ZXJtcyBmb3IgZWFjaCB0b3BpYywgd2UgYXNzaWduIGEgaGFzaHRhZyB0byBlYWNoIHRvcGljLiBJdCBpcyBlYXN5IHRvIGZpbmQgdGhhdCB0aGUgdG9waWMgMTMgY29udGFpbnMgc28gbWFueSB3b3JkcyBhYm91dCBlY29ub215LCBzdWNoIGFzIHRheCwgZGVidCwgcGF5LCBldGMuIEFuZCB0aGUgdG9waWMgMyBjb250YWlucyBtYW55IHdvcmRzIGFib3V0IG1pbGl0YXJ5LCBzdWNoIGFzIHdhciwgZm9yY2UsIG1pbGl0YXJ5LCBldGMuIFNvIHdlIGNhbiBkZWZpbml0ZWx5IGRlZmluZSB0aGUgdG9waWMgb2YgMTMgYW5kIDMgYXJlIGVjb25vbXkgYW5kIG1pbGl0YXJ5LCBhbmQgdGhlbiB3ZSB1c2UgdGhlIGZpcnN0IGNvbW1vbiB3b3JkcyBvZiBvdGhlciB0b3BpY3MgYXMgdGhlaXIgaGFzaHRhZy4gQW5kIHRoaXMgaXMgd2hhdCB3ZSBkbyBuZXh0LgoKYGBge3J9CnRvcGljcy5oYXNoPWFzLnZlY3RvcihsZGFPdXQudGVybXNbMSwgXSkKdG9waWNzLmhhc2hbMTNdIDwtICJlY29ub215Igp0b3BpY3MuaGFzaFsxNV0gPC0gImx1Y2t5Igp0b3BpY3MuaGFzaFszXSA8LSAibWlsaXRhcnkiCmNvcnB1cy5saXN0JGxkYXRvcGljPWFzLnZlY3RvcihsZGFPdXQudG9waWNzKQpjb3JwdXMubGlzdCRsZGFoYXNoPXRvcGljcy5oYXNoW2xkYU91dC50b3BpY3NdCgpjb2xuYW1lcyh0b3BpY1Byb2JhYmlsaXRpZXMpPXRvcGljcy5oYXNoCmNvcnB1cy5saXN0LmRmPWNiaW5kKGNvcnB1cy5saXN0LCB0b3BpY1Byb2JhYmlsaXRpZXMpCmBgYAoKIyMgQ2x1c3RlcmluZyBvZiB0b3BpY3MKYGBge3IsIGZpZy53aWR0aD0zLCBmaWcuaGVpZ2h0PTR9CnBhcihtYXI9YygxLDEsMSwxKSkKdG9waWMuc3VtbWFyeT10YmxfZGYoY29ycHVzLmxpc3QuZGYpJT4lCiAgICAgICAgICAgICAgc2VsZWN0KEZpbGUsIGdvdmVybjpuYXRpb24pJT4lCiAgICAgICAgICAgICAgZ3JvdXBfYnkoRmlsZSklPiUKICAgICAgICAgICAgICBzdW1tYXJpc2VfZWFjaChmdW5zKG1lYW4pKQp0b3BpYy5zdW1tYXJ5PWFzLmRhdGEuZnJhbWUodG9waWMuc3VtbWFyeSkKcm93bmFtZXModG9waWMuc3VtbWFyeSk9dG9waWMuc3VtbWFyeVssMV0KCnRvcGljLnBsb3Q9YygxLCAxMywgOSwgMTEsIDgsIDMsIDcpCnByaW50KHRvcGljcy5oYXNoW3RvcGljLnBsb3RdKQoKaGVhdG1hcC4yKGFzLm1hdHJpeCh0b3BpYy5zdW1tYXJ5Wyx0b3BpYy5wbG90KzFdKSwgCiAgICAgICAgICBzY2FsZSA9ICJjb2x1bW4iLCBrZXk9RiwgCiAgICAgICAgICBjb2wgPSBibHVlcmVkKDEwMCksCiAgICAgICAgICBjZXhSb3cgPSAwLjksIGNleENvbCA9IDAuOSwgbWFyZ2lucyA9IGMoOCwgOCksCiAgICAgICAgICB0cmFjZSA9ICJub25lIiwgZGVuc2l0eS5pbmZvID0gIm5vbmUiKQpgYGAKCgpOb3cgd2UgdGFrZSBmaXZlIHJlY2VudCBwcmVzaWRlbnRzIHRvIG1ha2UgdGhpcyBxdWVzdGlvbiBzaW1wbGVyLiBUaGUgbW9zdCByZWNlbnQgNSBwcmVzaWRlbnRzIGFyZSBHZW9yZ2VCdXNoLCBXaWxsaWFtSkNsaW50b24sIEdlb3JnZVdCdXNoLCBCYXJhY2tPYmFtYSwgRG9uYWxkSlRydW1wLiBOb3cgd2UgbWFrZSBhIHBsb3QgdG8gc2VlIHdoaWNoIHRvcGljIHRoZXkgcGF5IGF0dGVudGlvbiB0byBtb3JlLgoKYGBge3IsIGZpZy53aWR0aD0zLjMsIGZpZy5oZWlnaHQ9NX0KCnBhcihtZnJvdz1jKDUsIDEpLCBtYXI9YygxLDEsMiwwKSwgYnR5PSJuIiwgeGF4dD0ibiIsIHlheHQ9Im4iKQoKdG9waWMucGxvdD1jKDMsIDEzLCAxNCwgMTUsIDgsIDksIDEyKQpwcmludCh0b3BpY3MuaGFzaFt0b3BpYy5wbG90XSkKCnNwZWVjaC5kZj10YmxfZGYoY29ycHVzLmxpc3QuZGYpJT4lZmlsdGVyKEZpbGU9PSJHZW9yZ2VCdXNoIiwgdHlwZT09ImluYXVnIiwgVGVybT09MSklPiVzZWxlY3Qoc2VudC5pZCwgZ292ZXJuOm5hdGlvbikKc3BlZWNoLmRmPWFzLm1hdHJpeChzcGVlY2guZGYpCnNwZWVjaC5kZlssLTFdPXJlcGxhY2Uoc3BlZWNoLmRmWywtMV0sIHNwZWVjaC5kZlssLTFdPDEvMTUsIDAuMDAxKQpzcGVlY2guZGZbLC0xXT1mLnNtb290aC50b3BpYyh4PXNwZWVjaC5kZlssMV0sIHk9c3BlZWNoLmRmWywtMV0pCnBsb3Quc3RhY2tlZChzcGVlY2guZGZbLDFdLCBzcGVlY2guZGZbLHRvcGljLnBsb3QrMV0sCiAgICAgICAgICAgICB4bGFiPSJTZW50ZW5jZXMiLCB5bGFiPSJUb3BpYyBzaGFyZSIsIG1haW49Ikdlb3JnZSBCdXNoLCBpbmF1Z3VyYWwgU3BlZWNoZXMiKQoKc3BlZWNoLmRmPXRibF9kZihjb3JwdXMubGlzdC5kZiklPiVmaWx0ZXIoRmlsZT09IldpbGxpYW1KQ2xpbnRvbiIsIHR5cGU9PSJpbmF1ZyIsIFRlcm09PTEpJT4lc2VsZWN0KHNlbnQuaWQsIGdvdmVybjpuYXRpb24pCnNwZWVjaC5kZj1hcy5tYXRyaXgoc3BlZWNoLmRmKQpzcGVlY2guZGZbLC0xXT1yZXBsYWNlKHNwZWVjaC5kZlssLTFdLCBzcGVlY2guZGZbLC0xXTwxLzE1LCAwLjAwMSkKc3BlZWNoLmRmWywtMV09Zi5zbW9vdGgudG9waWMoeD1zcGVlY2guZGZbLDFdLCB5PXNwZWVjaC5kZlssLTFdKQpwbG90LnN0YWNrZWQoc3BlZWNoLmRmWywxXSwgc3BlZWNoLmRmWyx0b3BpYy5wbG90KzFdLAogICAgICAgICAgICAgeGxhYj0iU2VudGVuY2VzIiwgeWxhYj0iVG9waWMgc2hhcmUiLCBtYWluPSJXaWxsaWFtIEogQ2xpbnRvbiwgaW5hdWd1cmFsIFNwZWVjaGVzIikKCnNwZWVjaC5kZj10YmxfZGYoY29ycHVzLmxpc3QuZGYpJT4lZmlsdGVyKEZpbGU9PSJHZW9yZ2VXQnVzaCIsIHR5cGU9PSJpbmF1ZyIsIFRlcm09PTEpJT4lc2VsZWN0KHNlbnQuaWQsIGdvdmVybjpuYXRpb24pCnNwZWVjaC5kZj1hcy5tYXRyaXgoc3BlZWNoLmRmKQpzcGVlY2guZGZbLC0xXT1yZXBsYWNlKHNwZWVjaC5kZlssLTFdLCBzcGVlY2guZGZbLC0xXTwxLzE1LCAwLjAwMSkKc3BlZWNoLmRmWywtMV09Zi5zbW9vdGgudG9waWMoeD1zcGVlY2guZGZbLDFdLCB5PXNwZWVjaC5kZlssLTFdKQpwbG90LnN0YWNrZWQoc3BlZWNoLmRmWywxXSwgc3BlZWNoLmRmWyx0b3BpYy5wbG90KzFdLAogICAgICAgICAgICAgeGxhYj0iU2VudGVuY2VzIiwgeWxhYj0iVG9waWMgc2hhcmUiLCBtYWluPSJHZW9yZ2UgVy4gQnVzaCwgaW5hdWd1cmFsIFNwZWVjaGVzIikKCnNwZWVjaC5kZj10YmxfZGYoY29ycHVzLmxpc3QuZGYpJT4lZmlsdGVyKEZpbGU9PSJCYXJhY2tPYmFtYSIsIHR5cGU9PSJpbmF1ZyIsIFRlcm09PTEpJT4lc2VsZWN0KHNlbnQuaWQsIGdvdmVybjpuYXRpb24pCnNwZWVjaC5kZj1hcy5tYXRyaXgoc3BlZWNoLmRmKQpzcGVlY2guZGZbLC0xXT1yZXBsYWNlKHNwZWVjaC5kZlssLTFdLCBzcGVlY2guZGZbLC0xXTwxLzE1LCAwLjAwMSkKc3BlZWNoLmRmWywtMV09Zi5zbW9vdGgudG9waWMoeD1zcGVlY2guZGZbLDFdLCB5PXNwZWVjaC5kZlssLTFdKQpwbG90LnN0YWNrZWQoc3BlZWNoLmRmWywxXSwgc3BlZWNoLmRmWyx0b3BpYy5wbG90KzFdLAogICAgICAgICAgICAgeGxhYj0iU2VudGVuY2VzIiwgeWxhYj0iVG9waWMgc2hhcmUiLCBtYWluPSJCYXJhY2sgT2JhbWEsIGluYXVndXJhbCBTcGVlY2hlcyIpCgpzcGVlY2guZGY9dGJsX2RmKGNvcnB1cy5saXN0LmRmKSU+JWZpbHRlcihGaWxlPT0iRG9uYWxkSlRydW1wIiwgdHlwZT09ImluYXVnIiwgVGVybT09MSklPiVzZWxlY3Qoc2VudC5pZCwgZ292ZXJuOm5hdGlvbikKc3BlZWNoLmRmPWFzLm1hdHJpeChzcGVlY2guZGYpCnNwZWVjaC5kZlssLTFdPXJlcGxhY2Uoc3BlZWNoLmRmWywtMV0sIHNwZWVjaC5kZlssLTFdPDEvMTUsIDAuMDAxKQpzcGVlY2guZGZbLC0xXT1mLnNtb290aC50b3BpYyh4PXNwZWVjaC5kZlssMV0sIHk9c3BlZWNoLmRmWywtMV0pCnBsb3Quc3RhY2tlZChzcGVlY2guZGZbLDFdLCBzcGVlY2guZGZbLHRvcGljLnBsb3QrMV0sCiAgICAgICAgICAgICB4bGFiPSJTZW50ZW5jZXMiLCB5bGFiPSJUb3BpYyBzaGFyZSIsIG1haW49IkRvbmFsZCBKLiBUcnVtcCwgaW5hdWd1cmFsIFNwZWVjaGVzIikKYGBgCgpGcm9tIHRoZSBwbG90LCB3ZSBjYW4gZmluZCB0aGF0IEJhcmFjayBPYmFtYSdzIGZpc3QgaW5hdWd1cmFsIHRvcGljIGNvbnRhaW5zIG11Y2ggbW9yZSAiZWNvbm9teSIgdGhhbiBvdGhlciBwcmVzaWRlbnRzLCBidXQgd2h5PwpBcyB3ZSBrbm93LCBHbG9iYWwgZmluYW5jaWFsIGNyaXNpcyBoYXBwZW5kIGluIDIwMDgsIGFuZCBPYmFtYSBiZWNhbWUgdGhlIHByZXNpZGVudCBpbiAyMDA5LiBTbyBoZSBuZWVkZWQgdG8gdGFrZSBzb21lIGVjb25vbWljYWwgc3RyYXRlZ2llcyB0byByZXZpdmUgQW1lcmljYW4ncyBlY29ub215LgpTb21lb25lIG1heSBhc2sgdGhhdCBCYXJhY2sgT2JhbWEgdGFsa2VkIG1vcmUgYWJvdXQgZWNvbm9teSBiZWFjYXVzZSBoZSBwcmVmZXJlZCB0byBwYXkgYXR0ZW50aW9uIHRvIEFtZXJpY2FuIGVjb25vbXksIGJ1dCBub3QgYmVjYXVzZSB0aGUgMjAwOCBHbG9iYWwgZmluYW5jaWFsIGNyaXNpcyBoYXBwZW5lZC4KV2UgY2FuIG1ha2UgYSBmdXJ0aGVyIHBsb3QgdG8gY29tcGFyZSB0aGUgMXN0IHRlcm0gaW5hdWd1cmFsIHNwZWVjaCBhbmQgMm5kIHRlcm0gaW5hdWd1cmFsIHNwZWVjaCBvZiBCYXJhY2sgT2JhbWEgdG8gc2VlIHRoZSBzcGVjaWZpYyByZWFzb24uCgpgYGB7ciwgZmlnLmhlaWdodD0zfQpwYXIobWZyb3c9YygyLCAxKSwgbWFyPWMoMSwxLDIsMCksIGJ0eT0ibiIsIHhheHQ9Im4iLCB5YXh0PSJuIikKc3BlZWNoLmRmPXRibF9kZihjb3JwdXMubGlzdC5kZiklPiVmaWx0ZXIoRmlsZT09IkJhcmFja09iYW1hIiwgdHlwZT09ImluYXVnIiwgVGVybT09MSklPiVzZWxlY3Qoc2VudC5pZCwgZ292ZXJuOm5hdGlvbikKc3BlZWNoLmRmPWFzLm1hdHJpeChzcGVlY2guZGYpCnNwZWVjaC5kZlssLTFdPXJlcGxhY2Uoc3BlZWNoLmRmWywtMV0sIHNwZWVjaC5kZlssLTFdPDEvMTUsIDAuMDAxKQpzcGVlY2guZGZbLC0xXT1mLnNtb290aC50b3BpYyh4PXNwZWVjaC5kZlssMV0sIHk9c3BlZWNoLmRmWywtMV0pCnBsb3Quc3RhY2tlZChzcGVlY2guZGZbLDFdLCBzcGVlY2guZGZbLHRvcGljLnBsb3QrMV0sCiAgICAgICAgICAgICB4bGFiPSJTZW50ZW5jZXMiLCB5bGFiPSJUb3BpYyBzaGFyZSIsIG1haW49IkJhcmFjayBPYmFtYSwgaW5hdWd1cmFsIFNwZWVjaGVzLCAxc3QgdGVybSIpCgpzcGVlY2guZGY9dGJsX2RmKGNvcnB1cy5saXN0LmRmKSU+JWZpbHRlcihGaWxlPT0iQmFyYWNrT2JhbWEiLCB0eXBlPT0iaW5hdWciLCBUZXJtPT0yKSU+JXNlbGVjdChzZW50LmlkLCBnb3Zlcm46bmF0aW9uKQpzcGVlY2guZGY9YXMubWF0cml4KHNwZWVjaC5kZikKc3BlZWNoLmRmWywtMV09cmVwbGFjZShzcGVlY2guZGZbLC0xXSwgc3BlZWNoLmRmWywtMV08MS8xNSwgMC4wMDEpCnNwZWVjaC5kZlssLTFdPWYuc21vb3RoLnRvcGljKHg9c3BlZWNoLmRmWywxXSwgeT1zcGVlY2guZGZbLC0xXSkKcGxvdC5zdGFja2VkKHNwZWVjaC5kZlssMV0sIHNwZWVjaC5kZlssdG9waWMucGxvdCsxXSwKICAgICAgICAgICAgIHhsYWI9IlNlbnRlbmNlcyIsIHlsYWI9IlRvcGljIHNoYXJlIiwgbWFpbj0iQmFyYWNrIE9iYW1hLCBpbmF1Z3VyYWwgU3BlZWNoZXMsIDJuZCB0ZXJtIikKCmBgYAoKRnJvbSB0aGlzIHBsb3QsIHdlIGZpbmQgdGhhdCBjb21wYXJlZCB0byB0aGUgZmlyc3QgaW5hdWd1cmFsIHNwZWVjaCwgQmFyYWNrIE9iYW1hIHRhbGtlZCBtdWNoIGxlc3MgYWJvdXQgZWNvbm9teS4gU28gd2Uga25vdyB0aGF0IHRoZSByZWFzb24gQmFyYWNrIE9iYW1hIHRhbGtlZCBtdWNoIGFib3V0IGVjb25vbXkgaXMgdGhhdCBoZSBuZWVkZWQgdG8gdGFrZSBzb21lIHBvc2l0aXZlIGVjb25vbXkgc3RyYXRlZ3kgaW4gcmVzcG9uc2Ugb2YgdGhlIGdsb2JhbCBmaW5hbmNpYWwgY3Jpc2lzLiBTbywgSW4gb3RoZXIgd29yZHMsIGNhbiB3ZSBzYXkgdGhhdCBzb21lIGJpZyBldmVudHMgY2FuIGFmZmVjdCB0aGUgdG9waWMgb2YgcHJlc2lkZW50cycgaW5hdWd1cmFsIHNwZWVjaD8gTm93LCBsZXQncyBtYWtlIGEgZ3JhcGggY29tcGFyZWQgdGhlIGZpcnN0IGFuZCBzZWNvbmQgaW5hdWd1cmFsIHNwZWVjaCBvZiBHZW9yZ2VXQnVzaC4KCmBgYHtyfQpwYXIobWZyb3c9YygyLCAxKSwgbWFyPWMoMSwxLDIsMCksIGJ0eT0ibiIsIHhheHQ9Im4iLCB5YXh0PSJuIikKc3BlZWNoLmRmPXRibF9kZihjb3JwdXMubGlzdC5kZiklPiVmaWx0ZXIoRmlsZT09Ikdlb3JnZVdCdXNoIiwgdHlwZT09ImluYXVnIiwgVGVybT09MSklPiVzZWxlY3Qoc2VudC5pZCwgZ292ZXJuOm5hdGlvbikKc3BlZWNoLmRmPWFzLm1hdHJpeChzcGVlY2guZGYpCnNwZWVjaC5kZlssLTFdPXJlcGxhY2Uoc3BlZWNoLmRmWywtMV0sIHNwZWVjaC5kZlssLTFdPDEvMTUsIDAuMDAxKQpzcGVlY2guZGZbLC0xXT1mLnNtb290aC50b3BpYyh4PXNwZWVjaC5kZlssMV0sIHk9c3BlZWNoLmRmWywtMV0pCnBsb3Quc3RhY2tlZChzcGVlY2guZGZbLDFdLCBzcGVlY2guZGZbLHRvcGljLnBsb3QrMV0sCiAgICAgICAgICAgICB4bGFiPSJTZW50ZW5jZXMiLCB5bGFiPSJUb3BpYyBzaGFyZSIsIG1haW49Ikdlb3JnZSBXLiBCdXNoLCBpbmF1Z3VyYWwgU3BlZWNoZXMiKQpzcGVlY2guZGY9dGJsX2RmKGNvcnB1cy5saXN0LmRmKSU+JWZpbHRlcihGaWxlPT0iR2VvcmdlV0J1c2giLCB0eXBlPT0iaW5hdWciLCBUZXJtPT0yKSU+JXNlbGVjdChzZW50LmlkLCBnb3Zlcm46bmF0aW9uKQpzcGVlY2guZGY9YXMubWF0cml4KHNwZWVjaC5kZikKc3BlZWNoLmRmWywtMV09cmVwbGFjZShzcGVlY2guZGZbLC0xXSwgc3BlZWNoLmRmWywtMV08MS8xNSwgMC4wMDEpCnNwZWVjaC5kZlssLTFdPWYuc21vb3RoLnRvcGljKHg9c3BlZWNoLmRmWywxXSwgeT1zcGVlY2guZGZbLC0xXSkKcGxvdC5zdGFja2VkKHNwZWVjaC5kZlssMV0sIHNwZWVjaC5kZlssdG9waWMucGxvdCsxXSwKICAgICAgICAgICAgIHhsYWI9IlNlbnRlbmNlcyIsIHlsYWI9IlRvcGljIHNoYXJlIiwgbWFpbj0iR2VvcmdlIFcuIEJ1c2gsIGluYXVndXJhbCBTcGVlY2hlcyIpCgpgYGAKCkZyb20gdGhpcyBncmFwaCwgd2UgY2FuIGZpbmQgR2VvcmdlV0J1c2ggdGFsa2VkIG11Y2ggbW9yZSBhYm91dCBtaWxpdGFyeSBhdCB0aGUgYmVnaW5uaW5nIG9mIGhpcyB0b3BpYy4gQXMgd2Uga25vdywgIlNlcHRlbWJlciAxMSBhdHRhY2tzIiBoYXBwZW5lZCBpbiAyMDAxIHdoZW4gR2VvcmdlV0J1c2ggd2FzIGJlaW5nIHRoZSBwcmVzaWRlbnQgb2YgaGlzIGZpcnN0IHRlcm0uIFNvIGlmIGhlIHdhbnRlZCB0byBiZSB0aGUgcHJlc2lkZW50IGZvciB0aGUgc2Vjb25kIHRlcm0sIGhlIG11c3Qgc2F5IHNvbWV0aGluZyBhYm91dCBzb2x2aW5nIHRlcnJvcmlzdCBhdHRhY2suIFNvIHdlIGNhbiBob2xkIHRoZSBvcGluaW9uIHRoYXQgYmlnIGV2ZW50cyBjYW4gYWZmZWN0IHRoZSBpbmF1Z3VyYWwgc3BlZWNoZXMgb2YgcHJlc2lkZW50cyBtb3JlIG9yIGxlc3MuCgpOb3cgd2UgbWFrZSBmaXZlIGNsdXN0ZXJzIHRvIGNsYXNzaWZ5IGluYXVndXJhbCBzcGVlY2hlcyBvZiBhbGwgVVMgcHJlc2lkZW50cy4KCmBgYHtyLCBmaWcud2lkdGg9MywgZmlnLmhlaWdodD0zfQpwcmVzaWQuc3VtbWFyeT10YmxfZGYoY29ycHVzLmxpc3QuZGYpJT4lCiAgZmlsdGVyKHR5cGU9PSJpbmF1ZyIpJT4lCiAgc2VsZWN0KEZpbGUsIGdvdmVybjpuYXRpb24pJT4lCiAgZ3JvdXBfYnkoRmlsZSklPiUKICBzdW1tYXJpc2VfZWFjaChmdW5zKG1lYW4pKQoKcHJlc2lkLnN1bW1hcnk9YXMuZGF0YS5mcmFtZShwcmVzaWQuc3VtbWFyeSkKcm93bmFtZXMocHJlc2lkLnN1bW1hcnkpPWFzLmNoYXJhY3RlcigocHJlc2lkLnN1bW1hcnlbLDFdKSkKa20ucmVzPWttZWFucyhzY2FsZShwcmVzaWQuc3VtbWFyeVssLTFdKSwgaXRlci5tYXg9MjAwLAogICAgICAgICAgICAgIDUpCmZ2aXpfY2x1c3RlcihrbS5yZXMsIAogICAgICAgICAgICAgc3RhbmQ9VCwgcmVwZWw9IFRSVUUsCiAgICAgICAgICAgICBkYXRhID0gcHJlc2lkLnN1bW1hcnlbLC0xXSwKICAgICAgICAgICAgIHNob3cuY2x1c3QuY2VudD1GQUxTRSkKYGBgCgpGcm9tIHRoaXMgZ3JhcGgsIHdlIGZpbmQgdGhhdCB0aGUgZml2ZSBtb3N0IHJlY2VudCBwcmVzaWRlbnRzLCBHZW9yZ2VCdXNoLCBXaWxsaWFtSkNsaW50b24sIEdlb3JnZVdCdXNoLCBCYXJhY2tPYmFtYSwgRG9uYWxkSlRydW1wIGFyZSBpbiB0aGUgbGVmdCBjbHVzdGVyIG9mIHRoZSBncmFwaCBhYm92ZS4gSXQgc2VlbXMgdGhhdCBkaWZmZXJlbnQgZXJhIHdpbGwgbGVhZCB0byBkaWZmZXJlbnQgdG9waWMoaW4gb3RoZXIgd29yZHMsIHRoZXkgbWF5IGZhY2UgdGhlIHNhbWUgcHJvYmxlbSBkdXJpbmcgdGhlIHNhbWUgZXJhLikKCgojIyNwYXJ0Mzp3b3JkIGNsb3VkIyMjIwoKCkZpcnN0LCBsZXQgbWFrZSBhIHdvcmQgY2xvdWQgdG8gc2hvdyB0aGUgbW9zdCBjb21tb24gNTAgd29yZHMgb2YgZml2ZSBwcmVzaWRlbnRzIChHZW9yZ2VCdXNoLCBXaWxsaWFtSkNsaW50b24sIEdlb3JnZVdCdXNoLCBCYXJhY2tPYmFtYSwgRG9uYWxkSlRydW1wKSAnaW5hdWd1cmFsIHNwZWVjaGVzLgpgYGB7cn0KdGQxIDwtIHNlbnRlbmNlLmxpc3QgJT4lIGZpbHRlcihGaWxlICVpbiUgYygiQmFyYWNrT2JhbWEiLCAiRG9uYWxkSlRydW1wIiwgIkdlb3JnZVdCdXNoIiwgIldpbGxpYW1KQ2xpbnRvbiIsICJHZW9yZ2VCdXNoIiksIFRlcm0gPT0gMSkgJT4lIHNlbGVjdChGaWxlLCBzZW50ZW5jZXMpIAp0ZDEkc2VudGVuY2VzIDwtIGFzLmNoYXJhY3Rlcih0ZDEkc2VudGVuY2VzKQp0ZDEgPC0gdGQxICU+JSB1bm5lc3RfdG9rZW5zKHdvcmQsIHNlbnRlbmNlcykKdGQxIDwtIHRkMSAlPiUKICBhbnRpX2pvaW4oc3RvcF93b3JkcywgYnkgPSAid29yZCIpCnRkMSAlPiUKICBjb3VudCh3b3JkKSAlPiUKICB3aXRoKHdvcmRjbG91ZCh3b3JkLCBuLCBtYXgud29yZHMgPSA1MCwgY29sb3IgPSBjKCJwdXJwbGU0IiwgInJlZDQiLCAiYmxhY2siKSkpCmBgYAoKV2UgY2FuIGZpbmQgdGhlIHRvcCA4IG1vc3QgY29tbW9uIHdvcmRzIGFyZSBhbWVyaWNhLCB3b3JsZCwgbmF0aW9uLCBhbWVyaWNhbnMsIHBlb3BsZSwgYW1lcmljYW5zLCBhbWVyaWNhbiwgdGltZSwgY291bnRyeSwgcGVvcGxlLiBJdCBpcyBvYnZpb3VzIHRoYXQgdGhlIGluYXVndXJhbCBzcGVlY2hlcyBzaG91bGQgY29uY2VudHJhdGUgb24gd2hhdCB0aGV5IGNhbiBhY2hpZXZlIGZvciAiQW1lcmljYSIuCgoKTm93IHdlIGhhdmUgYSBsb29rIGF0IHdoaWNoIHByZXNpZGVudCBsaWtlIHRvIHVzZSB0aGVzZSBjb21tb24gd29yZHMgbW9zdC4KYGBge3J9CmZvbyA8LSB0ZDEgJT4lCiAgZ3JvdXBfYnkod29yZCwgRmlsZSkgJT4lCiAgY291bnQoKQoKYmFyIDwtIHRkMSAlPiUKICBncm91cF9ieSh3b3JkKSAlPiUKICBjb3VudCgpICU+JQogIHJlbmFtZShhbGwgPSBuKQoKZm9vICU+JQogIGxlZnRfam9pbihiYXIsIGJ5ID0gIndvcmQiKSAlPiUKICBhcnJhbmdlKGRlc2MoYWxsKSkgJT4lCiAgaGVhZCg4MCkgJT4lCiAgdW5ncm91cCgpICU+JQogIGdncGxvdChhZXMocmVvcmRlcih3b3JkLCBhbGwsIEZVTiA9IG1pbiksIG4sIGZpbGwgPSBGaWxlKSkgKwogICNnZ3Bsb3QoYWVzKHdvcmQsIG4pKSArCiAgZ2VvbV9jb2woKSArCiAgeGxhYihOVUxMKSArCiAgY29vcmRfZmxpcCgpICsKICBmYWNldF93cmFwKH4gRmlsZSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgpCYXJhY2sgT2JhbWEgdXNlZCAibmF0aW9uIiBtb3N0LCBEb25hbGRKVHJ1bXAgdXNlZCAiYW1lcmljYW4iIG1vc3QsIGFuZCBXaWxsaWFtSkNsaW50b24gdXNlZCAid29ybGQiIG1vc3QuCgoKV2UgY2FuIGRpdmUgZGVlcGVyIGludG8gdGhpcyBmdW5kYW1lbnRhbCBxdWVzdGlvbiBieSBkaXJlY3RseSBjb21wYXJpbmcgdGhlIHJlbGF0aXZlIGZyZXF1ZW5jeSBvZiB3b3JkIHVzZSBiZXR3ZWVuIHByZXNpZGVudHMuIEluIHRoaXMgZXhhbXBsZSwgd2Ugd2lsbCBjb21wYXJlIFdpbGxpYW1KQ2xpbnRvbiB0byB0aGUgb3RoZXIgcHJlc2lkZW50cy4gCmBgYHtyfQpmcmVxdWVuY3kxIDwtIHRkMSAlPiUKICBjb3VudChGaWxlLCB3b3JkKSAlPiUKICBncm91cF9ieShGaWxlKSAlPiUKICBtdXRhdGUoZnJlcSA9IG4gLyBzdW0obikpICU+JSAKICBzZWxlY3QoLW4pICU+JSAKICBzcHJlYWQoRmlsZSwgZnJlcSkgJT4lIAogIGdhdGhlcihGaWxlLCBmcmVxLCBCYXJhY2tPYmFtYSA6IEdlb3JnZVdCdXNoKSAlPiUKICBmaWx0ZXIoIWlzLm5hKGZyZXEpICYgIWlzLm5hKEZpbGUpKQoKZ2dwbG90KGZyZXF1ZW5jeTEsIGFlcyhmcmVxLCBXaWxsaWFtSkNsaW50b24gLCBjb2xvciA9IGFicyhXaWxsaWFtSkNsaW50b24gLSBmcmVxKSkpICsKICBnZW9tX2FibGluZShjb2xvciA9ICJncmF5NDAiLCBsdHkgPSAyKSArCiAgZ2VvbV9qaXR0ZXIoYWxwaGEgPSAwLjEsIHNpemUgPSAyLjUsIHdpZHRoID0gMC4xLCBoZWlnaHQgPSAwLjEpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gd29yZCksIGNoZWNrX292ZXJsYXAgPSBUUlVFLCB2anVzdCA9IDEuNSkgKwogIHNjYWxlX3hfbG9nMTAobGFiZWxzID0gcGVyY2VudF9mb3JtYXQoKSkgKwogIHNjYWxlX3lfbG9nMTAobGFiZWxzID0gcGVyY2VudF9mb3JtYXQoKSkgKwogICNzY2FsZV9jb2xvcl9ncmFkaWVudChsaW1pdHMgPSBjKDAsIDAuMDAxKSwgbG93ID0gImRhcmtzbGF0ZWdyYXk0IiwgaGlnaCA9ICJncmF5OTUiKSArCiAgZmFjZXRfd3JhcCh+RmlsZSwgbmNvbCA9IDIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArCiAgbGFicyh5ID0gIldpbGxpYW1KQ2xpbnRvbnAiLCB4ID0gTlVMTCkKCmBgYAoKSW4gdGhlc2UgcGxvdHMsIHdvcmRzIHRoYXQgYXJlIGNsb3NlIHRvIHRoZSBkYXNoZWQgbGluZSAob2YgZXF1YWwgZnJlcXVlbmN5KSBoYXZlIHNpbWlsYXIgZnJlcXVlbmNpZXMgaW4gdGhlIGNvcnJlc3BvbmRpbmcgcHJlc2lkZW50cy4gV29yZHMgdGhhdCBhcmUgZnVydGhlciBhbG9uZyBhIHBhcnRpY3VsYXIgcHJlc2lkZW50IGF4aXMgKHN1Y2ggYXMg4oCcY2hhbmdl4oCdIGZvciBXaWxsaWFtIEogQ2xpbnRvbiB2cyBCYXJhY2sgT2JhbWEpIGFyZSBtb3JlIGZyZXF1ZW50IGZvciB0aGF0IHByZXNpZGVudC4gVGhlIGJsdWUtZ3JheSBzY2FsZSBpbmRpY2F0ZXMgaG93IGRpZmZlcmVudCB0aGUgV2lsbGlhbUpDbGludG9uIGZyZXF1ZW5jeSBpcyBmcm9tIHRoZSBvdmVyYWxsIGZyZXF1ZW5jeSAod2l0aCBoaWdoZXIgcmVsYXRpdmUgZnJlcXVlbmNpZXMgYmVpbmcgbGlnaHRlcikuIFRoZSAoc2xpZ2h0bHkgaml0dGVyZWQpIHBvaW50cyBpbiB0aGUgYmFja2dyb3VuZCByZXByZXNlbnQgdGhlIGNvbXBsZXRlIHNldCBvZiAoaGlnaC1mcmVxdWVuY3kpIHdvcmRzLCB3aGVyZWFzIHRoZSBkaXNwbGF5ZWQgd29yZHMgaGF2ZSBiZWVuIGNob3NlbiB0byBhdm9pZCBvdmVybGFwLgoKVGhlIHBsb3RzIGdpdmUgdXMgYW5vdGhlciB1c2VmdWwgb3ZlcnZpZXcgYW5nbGUuIEZvciBpbnN0YW5jZSwgdGhleSBzdWdnZXN0IHRoYXQgV0lsbGlhbUpDbGludG9uIHVzZXMg4oCcZmVsbG934oCdIG1vcmUgdGhhbiBvdGhlciBmb3VyIHByZXNpZGVudHMsIGJ1dCBoZSB1c2VzIOKAnGFtZXJpY2Fuc+KAnSBhcyBtb3JlIG9mdGVuIHRoYW4gQmFyYWNrIE9iYW1hIGFuZCBHZW9yZ2UgQnVzaCBidXQgZXF1YWxseSBvZnRlbiBhcyBEb25hbGQgVHJ1bXAgYW5kIEdlb3JnZSBXIEJ1c2guCgoKIyMjI3BhcnQ0OnNlbnRpbWVudCBhbmFseXNpcyAjIyMjCgpgYGB7cn0KdGQxICU+JQogIGZpbHRlcihGaWxlID09ICJCYXJhY2tPYmFtYSIpICU+JQogIGlubmVyX2pvaW4oZ2V0X3NlbnRpbWVudHMoImJpbmciKSwgYnkgPSAid29yZCIpICU+JQogIGNvdW50KHdvcmQsIHNlbnRpbWVudCwgc29ydCA9IFRSVUUpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBncm91cF9ieShzZW50aW1lbnQpICU+JQogIHRvcF9uKDEwLCBuKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgbXV0YXRlKHdvcmQgPSByZW9yZGVyKHdvcmQsIG4pKSAlPiUKICBnZ3Bsb3QoYWVzKHdvcmQsIG4sIGZpbGwgPSBzZW50aW1lbnQpKSArCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIGZhY2V0X3dyYXAofnNlbnRpbWVudCwgc2NhbGVzID0gImZyZWVfeSIpICsKICBsYWJzKHkgPSAiQ29udHJpYnV0aW9uIHRvIG5lZ2F0aXZlL3Bvc2l0aXZlIHNlbnRpbWVudCIsIHggPSBOVUxMKSArCiAgY29vcmRfZmxpcCgpICsKICBnZ3RpdGxlKCJCYXJhY2sgT2JhbWEgLSBTZW50aW1lbnQgYW5hbHlzaXMiKQpgYGAKCmBgYHtyfQp0ZDEgJT4lCiAgZmlsdGVyKEZpbGUgPT0gIkRvbmFsZEpUcnVtcCIpICU+JQogIGlubmVyX2pvaW4oZ2V0X3NlbnRpbWVudHMoImJpbmciKSwgYnkgPSAid29yZCIpICU+JQogIGNvdW50KHdvcmQsIHNlbnRpbWVudCwgc29ydCA9IFRSVUUpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBncm91cF9ieShzZW50aW1lbnQpICU+JQogIHRvcF9uKDEwLCBuKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgbXV0YXRlKHdvcmQgPSByZW9yZGVyKHdvcmQsIG4pKSAlPiUKICBnZ3Bsb3QoYWVzKHdvcmQsIG4sIGZpbGwgPSBzZW50aW1lbnQpKSArCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIGZhY2V0X3dyYXAofnNlbnRpbWVudCwgc2NhbGVzID0gImZyZWVfeSIpICsKICBsYWJzKHkgPSAiQ29udHJpYnV0aW9uIHRvIG5lZ2F0aXZlL3Bvc2l0aXZlIHNlbnRpbWVudCIsIHggPSBOVUxMKSArCiAgY29vcmRfZmxpcCgpICsKICBnZ3RpdGxlKCJEb25hbGRKVHJ1bXAgLSBTZW50aW1lbnQgYW5hbHlzaXMiKQpgYGAKCiMjI0NvbXBhcmlzb24gd29yZGNsb3VkIyMjCkFuIGFsdGVybmF0aXZlIHdheSBvZiBwb3J0cmF5aW5nIHRoZSBtb3N0IGltcG9ydGFudCB3b3JkcyBmb3IgZWFjaCBzZW50aW1lbnQgaXMgdGhyb3VnaCBvdXIgZmF2b3VyaXRlIHdvcmQtY2xvdWRzLiBUaGlzIHVzZXMgdGhlIHJlc2hhcGUgbGlicmFyeS4gSGVyZSBhcmUgYWxsIHRoZSBwcmVzaWRlbnRzIGNvbWJpbmVkIGFzIGFuIGV4YW1wbGU6CmBgYHtyfQp0ZDEgJT4lCiAgaW5uZXJfam9pbihnZXRfc2VudGltZW50cygiYmluZyIpLCBieSA9ICJ3b3JkIikgJT4lCiAgY291bnQod29yZCwgc2VudGltZW50LCBzb3J0ID0gVFJVRSkgJT4lCiAgYWNhc3Qod29yZCB+IHNlbnRpbWVudCwgdmFsdWUudmFyID0gIm4iLCBmaWxsID0gMCkgJT4lCiAgY29tcGFyaXNvbi5jbG91ZChjb2xvcnMgPSBjKCIjRjg3NjZEIiwgIiMwMEJGQzQiKSwgbWF4LndvcmRzID0gNTApCgpgYGAKCldlIGNhbiBzZWUg4oCcaGFyZOKAnSBhbmQg4oCcZnJlZWRvbeKAnSBzcXVhcmluZyB1cCBhZ2FpbnN0IG9uZSBhbm90aGVyOyBsb2NrZWQgaW4gYW4gZXRlcm5hbCBiYXR0bGUgaW4gd2hpY2gg4oCcc3VjY2Vzc+KAnSBjb3VudGVycyDigJxnYWls4oCdIGFuZCDigJx1bml0eeKAnSByYWdlcyBhZ2FpbnN0IOKAnGJyZWFr4oCdLgoKCg==